Browse Source

修改地区选择组件

yuhongzhe
王泽彦 11 months ago
parent
commit
1167d9dbd5
  1. 8
      admin/.env.development
  2. 8
      admin/.env.production
  3. 3
      admin/.gitignore
  4. 3
      admin/auto-imports.d.ts
  5. 15
      admin/package.json
  6. 10
      admin/src/App.vue
  7. 58
      admin/src/addon/shop/api/delivery.ts
  8. 14
      admin/src/addon/shop/api/electronic_sheet.ts
  9. 207
      admin/src/addon/shop/api/goods.ts
  10. 125
      admin/src/addon/shop/api/marketing.ts
  11. 23
      admin/src/addon/shop/api/order.ts
  12. 21
      admin/src/addon/shop/api/shop_address.ts
  13. 1
      admin/src/addon/shop/lang/zh-cn/delivery.company_edit.json
  14. 1
      admin/src/addon/shop/lang/zh-cn/goods.list.json
  15. 2
      admin/src/addon/shop/lang/zh-cn/goods.virtual_edit.json
  16. 1
      admin/src/addon/shop/lang/zh-cn/marketing.exchange.goods_edit.json
  17. 244
      admin/src/addon/shop/views/address/edit.vue
  18. 133
      admin/src/addon/shop/views/address/list.vue
  19. 133
      admin/src/addon/shop/views/delivery/company.vue
  20. 306
      admin/src/addon/shop/views/delivery/company_edit.vue
  21. 73
      admin/src/addon/shop/views/delivery/components/delivery-personnel-edit.vue
  22. 94
      admin/src/addon/shop/views/delivery/config.vue
  23. 172
      admin/src/addon/shop/views/delivery/electronic_sheet.vue
  24. 139
      admin/src/addon/shop/views/delivery/electronic_sheet_config.vue
  25. 220
      admin/src/addon/shop/views/delivery/electronic_sheet_edit.vue
  26. 283
      admin/src/addon/shop/views/delivery/local.vue
  27. 80
      admin/src/addon/shop/views/delivery/search.vue
  28. 96
      admin/src/addon/shop/views/delivery/staff.vue
  29. 128
      admin/src/addon/shop/views/delivery/store.vue
  30. 244
      admin/src/addon/shop/views/delivery/store_edit.vue
  31. 132
      admin/src/addon/shop/views/delivery/template.vue
  32. 311
      admin/src/addon/shop/views/delivery/template_edit.vue
  33. 240
      admin/src/addon/shop/views/diy/components/edit-goods-coupon.vue
  34. 324
      admin/src/addon/shop/views/diy/components/edit-goods-list.vue
  35. 442
      admin/src/addon/shop/views/diy/components/edit-many-goods-list.vue
  36. 83
      admin/src/addon/shop/views/diy/components/edit-shop-exchange-goods.vue
  37. 1
      admin/src/addon/shop/views/diy/components/edit-shop-exchange-info.vue
  38. 115
      admin/src/addon/shop/views/diy/components/edit-shop-goods-ranking.vue
  39. 174
      admin/src/addon/shop/views/diy/components/edit-shop-goods-recommend.vue
  40. 1
      admin/src/addon/shop/views/diy/components/edit-shop-member-info.vue
  41. 391
      admin/src/addon/shop/views/diy/components/edit-shop-newcomer.vue
  42. 45
      admin/src/addon/shop/views/diy/components/edit-shop-order-info.vue
  43. 11
      admin/src/addon/shop/views/diy/components/edit-shop-search.vue
  44. 231
      admin/src/addon/shop/views/diy/components/edit-single-recommend.vue
  45. 204
      admin/src/addon/shop/views/goods/attr.vue
  46. 386
      admin/src/addon/shop/views/goods/attr_edit.vue
  47. 130
      admin/src/addon/shop/views/goods/brand_list.vue
  48. 135
      admin/src/addon/shop/views/goods/category.vue
  49. 183
      admin/src/addon/shop/views/goods/category_config.vue
  50. 77
      admin/src/addon/shop/views/goods/components/brand-edit.vue
  51. 92
      admin/src/addon/shop/views/goods/components/category-edit.vue
  52. 151
      admin/src/addon/shop/views/goods/components/coupon-select-popup.vue
  53. 92
      admin/src/addon/shop/views/goods/components/evaluate-add.vue
  54. 432
      admin/src/addon/shop/views/goods/components/goods-batch-settings-popup.vue
  55. 34
      admin/src/addon/shop/views/goods/components/goods-category-spread-popup.vue
  56. 209
      admin/src/addon/shop/views/goods/components/goods-member-price-popup.vue
  57. 168
      admin/src/addon/shop/views/goods/components/goods-price-edit-popup.vue
  58. 578
      admin/src/addon/shop/views/goods/components/goods-select-popup.vue
  59. 34
      admin/src/addon/shop/views/goods/components/goods-spread-popup.vue
  60. 116
      admin/src/addon/shop/views/goods/components/goods-stock-edit-popup.vue
  61. 120
      admin/src/addon/shop/views/goods/components/label-edit.vue
  62. 63
      admin/src/addon/shop/views/goods/components/label-group-edit.vue
  63. 578
      admin/src/addon/shop/views/goods/components/newcomer-goods-select-popup.vue
  64. 58
      admin/src/addon/shop/views/goods/components/service-edit.vue
  65. 271
      admin/src/addon/shop/views/goods/evaluate.vue
  66. 133
      admin/src/addon/shop/views/goods/label_group_list.vue
  67. 211
      admin/src/addon/shop/views/goods/label_list.vue
  68. 532
      admin/src/addon/shop/views/goods/list.vue
  69. 579
      admin/src/addon/shop/views/goods/public/js/useGoodsEdit.ts
  70. 1147
      admin/src/addon/shop/views/goods/real_edit.vue
  71. 215
      admin/src/addon/shop/views/goods/recycle.vue
  72. 95
      admin/src/addon/shop/views/goods/service.vue
  73. 1066
      admin/src/addon/shop/views/goods/virtual_edit.vue
  74. 181
      admin/src/addon/shop/views/index/index.vue
  75. 247
      admin/src/addon/shop/views/marketing/coupon/add.vue
  76. 133
      admin/src/addon/shop/views/marketing/coupon/components/coupon-collection.vue
  77. 34
      admin/src/addon/shop/views/marketing/coupon/components/coupon-spread-popup.vue
  78. 264
      admin/src/addon/shop/views/marketing/coupon/edit.vue
  79. 187
      admin/src/addon/shop/views/marketing/coupon/list.vue
  80. 569
      admin/src/addon/shop/views/marketing/discount/add.vue
  81. 375
      admin/src/addon/shop/views/marketing/discount/components/discount-detail.vue
  82. 362
      admin/src/addon/shop/views/marketing/discount/components/goods-sku-popup.vue
  83. 98
      admin/src/addon/shop/views/marketing/discount/config.vue
  84. 348
      admin/src/addon/shop/views/marketing/discount/detail.vue
  85. 572
      admin/src/addon/shop/views/marketing/discount/edit.vue
  86. 198
      admin/src/addon/shop/views/marketing/discount/list.vue
  87. 175
      admin/src/addon/shop/views/marketing/exchange/components/goods-select-popup.vue
  88. 115
      admin/src/addon/shop/views/marketing/exchange/components/goods-sku-select.vue
  89. 727
      admin/src/addon/shop/views/marketing/exchange/goods_add.vue
  90. 684
      admin/src/addon/shop/views/marketing/exchange/goods_edit.vue
  91. 199
      admin/src/addon/shop/views/marketing/exchange/goods_list.vue
  92. 172
      admin/src/addon/shop/views/marketing/exchange/order_list.vue
  93. 200
      admin/src/addon/shop/views/marketing/goods_rank/components/rank-select-popup.vue
  94. 108
      admin/src/addon/shop/views/marketing/goods_rank/config.vue
  95. 568
      admin/src/addon/shop/views/marketing/goods_rank/edit.vue
  96. 327
      admin/src/addon/shop/views/marketing/goods_rank/list.vue
  97. 34
      admin/src/addon/shop/views/marketing/index.vue
  98. 170
      admin/src/addon/shop/views/marketing/manjian/detail.vue
  99. 1089
      admin/src/addon/shop/views/marketing/manjian/edit.vue
  100. 381
      admin/src/addon/shop/views/marketing/manjian/list.vue

8
admin/.env.development

@ -0,0 +1,8 @@
# api请求地址
VITE_APP_BASE_URL='http://146.56.228.75:20024/adminapi/'
# 图片服务器地址
VITE_IMG_DOMAIN=''
# 请求时header中token的参数名
VITE_REQUEST_HEADER_TOKEN_KEY='token'

8
admin/.env.production

@ -0,0 +1,8 @@
# api请求地址
VITE_APP_BASE_URL='http://146.56.228.75:20024/adminapi/'
# 图片服务器地址
VITE_IMG_DOMAIN=''
# 请求时header中token的参数名
VITE_REQUEST_HEADER_TOKEN_KEY='token'

3
admin/.gitignore

@ -22,6 +22,3 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.env.development
.env.production

3
admin/auto-imports.d.ts

@ -1,6 +1,5 @@
// Generated by 'unplugin-auto-import'
export {}
declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElNotification: typeof import('element-plus/es')['ElNotification']
}

15
admin/package.json

@ -6,10 +6,7 @@
"scripts": {
"dev": "vite",
"build": "vite build && node publish.cjs",
"preview": "vite preview",
"lint": "eslint .",
"lint:fix": "eslint . --ext .ts --ext .vue --ext .js --fix",
"format": "prettier --write src/**/*.{ts,vue,js,json}"
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "2.0.10",
@ -41,25 +38,21 @@
"@tailwindcss/line-clamp": "0.4.2",
"@types/qrcode": "1.5.0",
"@types/sortablejs": "1.15.0",
"@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.32.1",
"@typescript-eslint/eslint-plugin": "5.53.0",
"@vitejs/plugin-vue": "4.0.0",
"autoprefixer": "10.4.13",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.5",
"eslint": "8.34.0",
"eslint-config-standard-with-typescript": "34.0.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-n": "15.6.1",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-vue": "^10.1.0",
"eslint-plugin-vue": "9.9.0",
"postcss": "8.4.21",
"prettier": "^3.5.3",
"tailwindcss": "3.2.4",
"typescript": "4.9.5",
"unplugin-auto-import": "0.13.0",
"unplugin-vue-components": "0.23.0",
"vite": "4.1.0",
"vue-eslint-parser": "^10.1.3",
"vue-tsc": "1.0.24"
}
}

10
admin/src/App.vue

@ -22,15 +22,11 @@ const locale = computed(() => (systemStore.lang === 'zh-cn' ? zhCn : en))
const toggleDark = useToggle(useDark())
watch(
route,
() => {
useAppStore().$patch((state) => {
watch(route, () => {
useAppStore().$patch(state => {
state.route = route
})
},
{ immediate: true }
)
}, { immediate: true })
onMounted(() => {
//

58
admin/src/addon/shop/api/delivery.ts

@ -25,7 +25,7 @@ export function getCompanyList(params: Record<string, any>) {
* @returns
*/
export function getCompanyInfo(company_id: number) {
return request.get(`shop/delivery/company/${company_id}`)
return request.get(`shop/delivery/company/${ company_id }`);
}
/**
@ -34,10 +34,7 @@ export function getCompanyInfo(company_id: number) {
* @returns
*/
export function addCompany(params: Record<string, any>) {
return request.post('shop/delivery/company', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/delivery/company', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -48,7 +45,7 @@ export function addCompany(params: Record<string, any>) {
export function editCompany(params: Record<string, any>) {
return request.put(`shop/delivery/company/${ params.company_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -58,12 +55,10 @@ export function editCompany(params: Record<string, any>) {
* @returns
*/
export function deleteCompany(company_id: number) {
return request.delete(`shop/delivery/company/${company_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/delivery/company/${ company_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/********************************* 运费模版 ***************************************/
/**
*
@ -89,7 +84,7 @@ export function getShippingTemplateList(params: Record<string, any>) {
* @returns
*/
export function getShippingTemplateInfo(template_id: number) {
return request.get(`shop/shipping/template/${template_id}`)
return request.get(`shop/shipping/template/${ template_id }`);
}
/**
@ -98,10 +93,7 @@ export function getShippingTemplateInfo(template_id: number) {
* @returns
*/
export function addShippingTemplate(params: Record<string, any>) {
return request.post('shop/shipping/template', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/shipping/template', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -112,7 +104,7 @@ export function addShippingTemplate(params: Record<string, any>) {
export function editShippingTemplate(params: Record<string, any>) {
return request.put(`shop/shipping/template/${ params.template_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -124,10 +116,11 @@ export function editShippingTemplate(params: Record<string, any>) {
export function deleteShippingTemplate(template_id: number) {
return request.delete(`shop/shipping/template/${ template_id }`, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
/********************************* 自提门店 ***************************************/
/**
*
@ -144,7 +137,7 @@ export function getStoreList(params: Record<string, any>) {
* @returns
*/
export function getStoreInfo(store_id: number) {
return request.get(`shop/delivery/store/${store_id}`)
return request.get(`shop/delivery/store/${ store_id }`);
}
/**
@ -153,10 +146,7 @@ export function getStoreInfo(store_id: number) {
* @returns
*/
export function addStore(params: Record<string, any>) {
return request.post('shop/delivery/store', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/delivery/store', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -167,7 +157,7 @@ export function addStore(params: Record<string, any>) {
export function editStore(params: Record<string, any>) {
return request.put(`shop/delivery/store/${ params.store_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -177,10 +167,7 @@ export function editStore(params: Record<string, any>) {
* @returns
*/
export function deleteStore(store_id: number) {
return request.delete(`shop/delivery/store/${store_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/delivery/store/${ store_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/********************************* 物流查询 ***************************************/
@ -190,10 +177,7 @@ export function deleteStore(store_id: number) {
* @returns
*/
export function setDeliverySearch(params: Record<string, any>) {
return request.post('shop/delivery/search', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/delivery/search', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -236,7 +220,7 @@ export function getShopDelivery(params: Record<string, any>) {
* @returns
*/
export function getShopDeliverInfo(staff_id: number) {
return request.get(`shop/delivery/staff/${staff_id}`)
return request.get(`shop/delivery/staff/${ staff_id }`);
}
/**
@ -245,9 +229,7 @@ export function getShopDeliverInfo(staff_id: number) {
* @returns
*/
export function addShopDeliver(params: Record<string, any>) {
return request.post('shop/delivery/staff', params, {
showSuccessMessage: true,
})
return request.post('shop/delivery/staff', params, { showSuccessMessage: true })
}
/**
@ -256,9 +238,7 @@ export function addShopDeliver(params: Record<string, any>) {
* @returns
*/
export function editShopDeliver(params: Record<string, any>) {
return request.put(`shop/delivery/staff/${params.deliver_id}`, params, {
showSuccessMessage: true,
})
return request.put(`shop/delivery/staff/${ params.deliver_id }`, params, { showSuccessMessage: true })
}
/**
@ -275,7 +255,7 @@ export function deleteShopDeliver(staff_id: number) {
* @returns
*/
export function getLocal() {
return request.get('shop/local')
return request.get('shop/local');
}
/**

14
admin/src/addon/shop/api/electronic_sheet.ts

@ -24,7 +24,7 @@ export function getElectronicSheetList(params: Record<string, any>) {
* @returns
*/
export function getElectronicSheetInfo(id: number) {
return request.get(`shop/electronic_sheet/${id}`)
return request.get(`shop/electronic_sheet/${ id }`);
}
/**
@ -35,7 +35,7 @@ export function getElectronicSheetInfo(id: number) {
export function addElectronicSheet(params: Record<string, any>) {
return request.post('shop/electronic_sheet', params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -47,7 +47,7 @@ export function addElectronicSheet(params: Record<string, any>) {
export function editElectronicSheet(params: Record<string, any>) {
return request.put(`shop/electronic_sheet/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -59,7 +59,7 @@ export function editElectronicSheet(params: Record<string, any>) {
export function deleteElectronicSheet(id: number) {
return request.delete(`shop/electronic_sheet/${ id }`, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -71,7 +71,7 @@ export function deleteElectronicSheet(id: number) {
export function setDefaultElectronicSheet(params: Record<string, any>) {
return request.put(`shop/electronic_sheet/setDefault/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -83,7 +83,7 @@ export function setDefaultElectronicSheet(params: Record<string, any>) {
export function setElectronicSheetConfig(params: Record<string, any>) {
return request.post('shop/electronic_sheet/config', params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -111,6 +111,6 @@ export function getElectronicSheetPayType() {
export function printElectronicSheet(params: Record<string, any>) {
return request.post('shop/electronic_sheet/print', params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}

207
admin/src/addon/shop/api/goods.ts

@ -15,7 +15,7 @@ export function getGoodsPageList(params: Record<string, any>) {
* @returns
*/
export function getGoodsInfo(goods_id: number) {
return request.get(`shop/goods/${goods_id}`)
return request.get(`shop/goods/${ goods_id }`);
}
/**
@ -24,10 +24,7 @@ export function getGoodsInfo(goods_id: number) {
* @returns
*/
export function addGoods(params: Record<string, any>) {
return request.post('shop/goods', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -35,10 +32,7 @@ export function addGoods(params: Record<string, any>) {
* @param params
*/
export function editGoods(params: Record<string, any>) {
return request.put(`shop/goods/${params.goods_id}`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.put(`shop/goods/${ params.goods_id }`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -46,7 +40,7 @@ export function editGoods(params: Record<string, any>) {
* @param params
*/
export function getGoodsInit(params: Record<string, any>) {
return request.get(`shop/goods/init`, { params })
return request.get(`shop/goods/init`, { params });
}
/**
@ -55,10 +49,7 @@ export function getGoodsInit(params: Record<string, any>) {
* @returns
*/
export function addVirtualGoods(params: Record<string, any>) {
return request.post('shop/goods/virtual', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods/virtual', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -68,7 +59,7 @@ export function addVirtualGoods(params: Record<string, any>) {
export function editVirtualGoods(params: Record<string, any>) {
return request.put(`shop/goods/virtual/${ params.goods_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -77,7 +68,7 @@ export function editVirtualGoods(params: Record<string, any>) {
* @param params
*/
export function getVirtualGoodsInit(params: Record<string, any>) {
return request.get(`shop/goods/virtual/init`, { params })
return request.get(`shop/goods/virtual/init`, { params });
}
/**
@ -86,10 +77,7 @@ export function getVirtualGoodsInit(params: Record<string, any>) {
* @returns
*/
export function deleteGoods(params: Record<string, any>) {
return request.put(`shop/goods/delete`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.put(`shop/goods/delete`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -107,10 +95,7 @@ export function getRecycleGoodsPageList(params: Record<string, any>) {
* @returns
*/
export function recycleGoods(params: Record<string, any>) {
return request.put(`shop/goods/recycle`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.put(`shop/goods/recycle`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -134,9 +119,7 @@ export function editGoodsStatus(params: Record<string, any>) {
* @param params
*/
export function copyGoods(params: Record<string, any>) {
return request.put(`shop/goods/copy/${params.goods_id}`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/copy/${ params.goods_id }`, params, { showSuccessMessage: true })
}
/**
@ -180,9 +163,7 @@ export function getActiveGoodsCount(params: Record<string, any>) {
* @returns
*/
export function editGoodsListStock(params: Record<string, any>) {
return request.put(`shop/goods/sku/stock`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/sku/stock`, params, { showSuccessMessage: true })
}
/**
@ -191,9 +172,7 @@ export function editGoodsListStock(params: Record<string, any>) {
* @returns
*/
export function editGoodsListPrice(params: Record<string, any>) {
return request.put(`shop/goods/sku/price`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/sku/price`, params, { showSuccessMessage: true })
}
/**
@ -202,9 +181,7 @@ export function editGoodsListPrice(params: Record<string, any>) {
* @returns
*/
export function editGoodsListMemberPrice(params: Record<string, any>) {
return request.put(`shop/goods/sku/member_price`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/sku/member_price`, params, { showSuccessMessage: true })
}
/**
@ -212,7 +189,7 @@ export function editGoodsListMemberPrice(params: Record<string, any>) {
* @returns
*/
export function getGoodsType() {
return request.get(`shop/goods/type`)
return request.get(`shop/goods/type`);
}
/**
@ -239,7 +216,7 @@ export function getLabelList(params: Record<string, any>) {
* @returns
*/
export function getLabelInfo(label_id: number) {
return request.get(`shop/goods/label/${label_id}`)
return request.get(`shop/goods/label/${ label_id }`);
}
/**
@ -248,10 +225,7 @@ export function getLabelInfo(label_id: number) {
* @returns
*/
export function addLabel(params: Record<string, any>) {
return request.post('shop/goods/label', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods/label', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -262,7 +236,7 @@ export function addLabel(params: Record<string, any>) {
export function editLabel(params: Record<string, any>) {
return request.put(`shop/goods/label/${ params.label_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -271,9 +245,7 @@ export function editLabel(params: Record<string, any>) {
* @param params
*/
export function modifyLabelStatus(params: Record<string, any>) {
return request.put(`shop/goods/label/status`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/label/status`, params, { showSuccessMessage: true })
}
/**
@ -282,10 +254,7 @@ export function modifyLabelStatus(params: Record<string, any>) {
* @returns
*/
export function deleteLabel(label_id: number) {
return request.delete(`shop/goods/label/${label_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/goods/label/${ label_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -293,9 +262,7 @@ export function deleteLabel(label_id: number) {
* @param params
*/
export function modifyLabelSort(params: Record<string, any>) {
return request.put(`shop/goods/label/sort`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/label/sort`, params, { showSuccessMessage: true })
}
/**
@ -322,7 +289,7 @@ export function getLabelGroupList(params: Record<string, any>) {
* @returns
*/
export function getLabelGroupInfo(label_id: number) {
return request.get(`shop/goods/label/group/${label_id}`)
return request.get(`shop/goods/label/group/${ label_id }`);
}
/**
@ -331,10 +298,7 @@ export function getLabelGroupInfo(label_id: number) {
* @returns
*/
export function addLabelGroup(params: Record<string, any>) {
return request.post('shop/goods/label/group', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods/label/group', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -345,7 +309,7 @@ export function addLabelGroup(params: Record<string, any>) {
export function editLabelGroup(params: Record<string, any>) {
return request.put(`shop/goods/label/group/${ params.group_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -355,10 +319,7 @@ export function editLabelGroup(params: Record<string, any>) {
* @returns
*/
export function deleteLabelGroup(group_id: number) {
return request.delete(`shop/goods/label/group/${group_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/goods/label/group/${ group_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -366,9 +327,7 @@ export function deleteLabelGroup(group_id: number) {
* @param params
*/
export function modifyLabelGroupSort(params: Record<string, any>) {
return request.put(`shop/goods/label/group/sort`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/label/group/sort`, params, { showSuccessMessage: true })
}
/**
@ -395,7 +354,7 @@ export function getBrandList(params: Record<string, any>) {
* @returns
*/
export function getBrandInfo(brand_id: number) {
return request.get(`shop/goods/brand/${brand_id}`)
return request.get(`shop/goods/brand/${ brand_id }`);
}
/**
@ -404,10 +363,7 @@ export function getBrandInfo(brand_id: number) {
* @returns
*/
export function addBrand(params: Record<string, any>) {
return request.post('shop/goods/brand', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods/brand', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -418,7 +374,7 @@ export function addBrand(params: Record<string, any>) {
export function editBrand(params: Record<string, any>) {
return request.put(`shop/goods/brand/${ params.brand_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -427,9 +383,7 @@ export function editBrand(params: Record<string, any>) {
* @param params
*/
export function modifyBrandSort(params: Record<string, any>) {
return request.put(`shop/goods/brand/sort`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/brand/sort`, params, { showSuccessMessage: true })
}
/**
@ -438,10 +392,7 @@ export function modifyBrandSort(params: Record<string, any>) {
* @returns
*/
export function deleteBrand(brand_id: number) {
return request.delete(`shop/goods/brand/${brand_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/goods/brand/${ brand_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -468,7 +419,7 @@ export function getServeList(params: Record<string, any>) {
* @returns
*/
export function getServeInfo(service_id: number) {
return request.get(`shop/goods/service/${service_id}`)
return request.get(`shop/goods/service/${ service_id }`);
}
/**
@ -477,10 +428,7 @@ export function getServeInfo(service_id: number) {
* @returns
*/
export function addServe(params: Record<string, any>) {
return request.post('shop/goods/service', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods/service', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -491,7 +439,7 @@ export function addServe(params: Record<string, any>) {
export function editServe(params: Record<string, any>) {
return request.put(`shop/goods/service/${ params.service_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -501,10 +449,7 @@ export function editServe(params: Record<string, any>) {
* @returns
*/
export function deleteServe(service_id: number) {
return request.delete(`shop/goods/service/${service_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/goods/service/${ service_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -530,7 +475,7 @@ export function getCategoryList(params: Record<string, any>) {
* @returns
*/
export function getCategoryInfo(category_id: number) {
return request.get(`shop/goods/category/${category_id}`)
return request.get(`shop/goods/category/${ category_id }`);
}
/**
@ -539,10 +484,7 @@ export function getCategoryInfo(category_id: number) {
* @returns
*/
export function addCategory(params: Record<string, any>) {
return request.post('shop/goods/category', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods/category', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -553,7 +495,7 @@ export function addCategory(params: Record<string, any>) {
export function editCategory(params: Record<string, any>) {
return request.put(`shop/goods/category/${ params.category_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -563,10 +505,7 @@ export function editCategory(params: Record<string, any>) {
* @returns
*/
export function deleteCategory(category_id: number) {
return request.delete(`shop/goods/category/${category_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/goods/category/${ category_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -575,10 +514,7 @@ export function deleteCategory(category_id: number) {
* @returns
*/
export function updateCategory(params: Record<string, any>) {
return request.post(`shop/goods/category/update`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post(`shop/goods/category/update`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -587,10 +523,7 @@ export function updateCategory(params: Record<string, any>) {
* @returns
*/
export function setCategoryConfig(params: Record<string, any>) {
return request.post(`shop/goods/category/config`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post(`shop/goods/category/config`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -598,7 +531,7 @@ export function setCategoryConfig(params: Record<string, any>) {
* @returns
*/
export function getCategoryConfig() {
return request.get(`shop/goods/category/config`)
return request.get(`shop/goods/category/config`);
}
/**
@ -633,10 +566,7 @@ export function getEvaluateList(params: Record<string, any>) {
* @returns
*/
export function addEvaluate(params: Record<string, any>) {
return request.post('shop/goods/evaluate', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods/evaluate', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -645,10 +575,7 @@ export function addEvaluate(params: Record<string, any>) {
* @returns
*/
export function deleteEvaluate(evaluate_id: number) {
return request.delete(`shop/goods/evaluate/${evaluate_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/goods/evaluate/${ evaluate_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -659,7 +586,7 @@ export function deleteEvaluate(evaluate_id: number) {
export function adoptEvaluate(evaluate_id: number) {
return request.put(`shop/goods/evaluate/adopt/${ evaluate_id }`, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -671,7 +598,7 @@ export function adoptEvaluate(evaluate_id: number) {
export function refuseEvaluate(evaluate_id: number) {
return request.put(`shop/goods/evaluate/refuse/${ evaluate_id }`, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -681,14 +608,10 @@ export function refuseEvaluate(evaluate_id: number) {
* @returns
*/
export function replyEvaluate(params: Record<string, any>) {
return request.put(
`shop/goods/evaluate/reply/${params.evaluate_id}`,
params,
{
return request.put(`shop/goods/evaluate/reply/${ params.evaluate_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
}
)
showSuccessMessage: true
})
}
/**
@ -699,7 +622,7 @@ export function replyEvaluate(params: Record<string, any>) {
export function toppingEvaluate(evaluate_id: number) {
return request.put(`shop/goods/evaluate/topping/${ evaluate_id }`, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -711,7 +634,7 @@ export function toppingEvaluate(evaluate_id: number) {
export function cancelToppingEvaluate(evaluate_id: number) {
return request.put(`shop/goods/evaluate/cancel_topping/${ evaluate_id }`, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -739,7 +662,7 @@ export function getAttrList(params: Record<string, any>) {
* @returns
*/
export function getAttrInfo(attr_id: number) {
return request.get(`shop/goods/attr/${attr_id}`)
return request.get(`shop/goods/attr/${ attr_id }`);
}
/**
@ -748,10 +671,7 @@ export function getAttrInfo(attr_id: number) {
* @returns
*/
export function addAttr(params: Record<string, any>) {
return request.post('shop/goods/attr', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/goods/attr', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -762,7 +682,7 @@ export function addAttr(params: Record<string, any>) {
export function editAttr(params: Record<string, any>) {
return request.put(`shop/goods/attr/${ params.attr_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -772,10 +692,7 @@ export function editAttr(params: Record<string, any>) {
* @returns
*/
export function deleteAttr(attr_id: number) {
return request.delete(`shop/goods/attr/${attr_id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/goods/attr/${ attr_id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -783,9 +700,7 @@ export function deleteAttr(attr_id: number) {
* @param params
*/
export function modifyAttrSort(params: Record<string, any>) {
return request.put(`shop/goods/attr/sort`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/attr/sort`, params, { showSuccessMessage: true })
}
/**
@ -793,9 +708,7 @@ export function modifyAttrSort(params: Record<string, any>) {
* @param params
*/
export function modifyAttrName(params: Record<string, any>) {
return request.put(`shop/goods/attr/attr_name`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/attr/attr_name`, params, { showSuccessMessage: true })
}
/**
@ -803,9 +716,7 @@ export function modifyAttrName(params: Record<string, any>) {
* @param params
*/
export function modifyAttrValue(params: Record<string, any>) {
return request.put(`shop/goods/attr/attr_value`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/attr/attr_value`, params, { showSuccessMessage: true })
}
/**
@ -821,7 +732,5 @@ export function getGoodsBatchSetDict() {
* @param params
*/
export function goodsBatchSet(params: Record<string, any>) {
return request.put(`shop/goods/batchSet`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/batchSet`, params, { showSuccessMessage: true })
}

125
admin/src/addon/shop/api/marketing.ts

@ -24,10 +24,7 @@ export function getGoodsCategoryList(params: Record<string, any>) {
* @returns
*/
export function addCoupon(params: Record<string, any>) {
return request.post(`shop/goods/coupon`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post(`shop/goods/coupon`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -62,7 +59,7 @@ export function getCouponSelectList(params: Record<string, any>) {
* @returns
*/
export function getCouponRecords(params: Record<string, any>) {
return request.get(`shop/goods/coupon/records`, { params })
return request.get(`shop/goods/coupon/records`, { params });
}
/**
@ -71,7 +68,7 @@ export function getCouponRecords(params: Record<string, any>) {
* @returns
*/
export function getCouponInfo(id: number) {
return request.get(`shop/goods/coupon/detail/${id}`)
return request.get(`shop/goods/coupon/detail/${ id }`);
}
/**
@ -80,9 +77,7 @@ export function getCouponInfo(id: number) {
* @returns
*/
export function editCouponStatus(params: Record<string, any>) {
return request.put(`shop/goods/coupon/setstatus/${params.status}`, params, {
showSuccessMessage: true,
})
return request.put(`shop/goods/coupon/setstatus/${ params.status }`, params, { showSuccessMessage: true })
}
/**
@ -93,7 +88,7 @@ export function editCouponStatus(params: Record<string, any>) {
export function editCoupon(params: Record<string, any>) {
return request.put(`shop/goods/coupon/edit/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -112,9 +107,7 @@ export function deleteCoupon(id: number) {
* @returns
*/
export function closeCoupon(id: number) {
return request.put(`shop/goods/coupon/invalid/${id}`, {
showSuccessMessage: true,
})
return request.put(`shop/goods/coupon/invalid/${ id }`, { showSuccessMessage: true })
}
/**
@ -150,7 +143,7 @@ export function getActiveDiscountStatusList() {
* @returns
*/
export function getActiveDiscountInfo(active_id: number) {
return request.get(`shop/active/discount/${active_id}`)
return request.get(`shop/active/discount/${ active_id }`);
}
/**
@ -159,10 +152,7 @@ export function getActiveDiscountInfo(active_id: number) {
* @returns
*/
export function addActiveDiscount(params: Record<string, any>) {
return request.post('shop/active/discount', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/active/discount', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -173,7 +163,7 @@ export function addActiveDiscount(params: Record<string, any>) {
export function editActiveDiscount(params: Record<string, any>) {
return request.put(`shop/active/discount/${ params.active_id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -183,14 +173,10 @@ export function editActiveDiscount(params: Record<string, any>) {
* @returns
*/
export function closeActiveDiscount(active_id: number) {
return request.put(
`shop/active/discount/close/${active_id}`,
{},
{
return request.put(`shop/active/discount/close/${ active_id }`, {}, {
showErrorMessage: true,
showSuccessMessage: true,
}
)
showSuccessMessage: true
})
}
/**
@ -199,9 +185,7 @@ export function closeActiveDiscount(active_id: number) {
* @returns
*/
export function deleteActiveDiscount(active_id: number) {
return request.delete(`shop/active/discount/${active_id}`, {
showSuccessMessage: true,
})
return request.delete(`shop/active/discount/${ active_id }`, { showSuccessMessage: true })
}
/**
@ -210,9 +194,7 @@ export function deleteActiveDiscount(active_id: number) {
* @returns
*/
export function getActiveDiscountGoodsPageList(params: Record<string, any>) {
return request.get(`shop/active/discount/goods/${params.active_id}`, {
params,
})
return request.get(`shop/active/discount/goods/${ params.active_id }`, { params })
}
/**
@ -221,9 +203,7 @@ export function getActiveDiscountGoodsPageList(params: Record<string, any>) {
* @returns
*/
export function getActiveDiscountOrderPageList(params: Record<string, any>) {
return request.get(`shop/active/discount/order/${params.active_id}`, {
params,
})
return request.get(`shop/active/discount/order/${ params.active_id }`, { params })
}
/**
@ -232,9 +212,7 @@ export function getActiveDiscountOrderPageList(params: Record<string, any>) {
* @returns
*/
export function getActiveDiscountMemberPageList(params: Record<string, any>) {
return request.get(`shop/active/discount/member/${params.active_id}`, {
params,
})
return request.get(`shop/active/discount/member/${ params.active_id }`, { params })
}
/**
@ -242,7 +220,7 @@ export function getActiveDiscountMemberPageList(params: Record<string, any>) {
* @returns
*/
export function getActiveDiscountConfig() {
return request.get(`shop/active/discount/config`)
return request.get(`shop/active/discount/config`);
}
/**
@ -253,7 +231,7 @@ export function getActiveDiscountConfig() {
export function editActiveDiscountConfig(params: Record<string, any>) {
return request.put(`shop/active/discount/config`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -274,7 +252,7 @@ export function getActiveExchangePageList(params: Record<string, any>) {
* @returns
*/
export function getActiveExchangeInfo(id: number) {
return request.get(`shop/active/exchange/${id}`)
return request.get(`shop/active/exchange/${ id }`);
}
/**
@ -283,10 +261,7 @@ export function getActiveExchangeInfo(id: number) {
* @returns
*/
export function addActiveExchange(params: Record<string, any>) {
return request.post('shop/active/exchange', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/active/exchange', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -297,7 +272,7 @@ export function addActiveExchange(params: Record<string, any>) {
export function editActiveExchange(params: Record<string, any>) {
return request.put(`shop/active/exchange/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -309,7 +284,7 @@ export function editActiveExchange(params: Record<string, any>) {
export function editActiveExchangeStatus(params: Record<string, any>) {
return request.put(`shop/active/exchange/status/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -319,9 +294,7 @@ export function editActiveExchangeStatus(params: Record<string, any>) {
* @returns
*/
export function deleteActiveExchange(id: number) {
return request.delete(`shop/active/exchange/${id}`, {
showSuccessMessage: true,
})
return request.delete(`shop/active/exchange/${ id }`, { showSuccessMessage: true })
}
/**
@ -339,7 +312,7 @@ export function getActiveExchangeStatus() {
* @returns
*/
export function getActiveNewcomerConfig() {
return request.get(`shop/active/newcomer/config`)
return request.get(`shop/active/newcomer/config`);
}
/**
@ -350,7 +323,7 @@ export function getActiveNewcomerConfig() {
export function editActiveNewcomerConfig(params: Record<string, any>) {
return request.put(`shop/active/newcomer/config`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -380,10 +353,7 @@ export function getNewcomerSelectGoodsList(params: Record<string, any>) {
* @returns
*/
export function setRankConfig(params: Record<string, any>) {
return request.post('shop/good/rank/config', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/good/rank/config', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -417,7 +387,7 @@ export function optionData() {
* @returns
*/
export function getRankInfo(rank_id: number) {
return request.get(`shop/good/rank/${rank_id}`)
return request.get(`shop/good/rank/${ rank_id }`);
}
/**
@ -426,10 +396,7 @@ export function getRankInfo(rank_id: number) {
* @returns
*/
export function addGoodRank(params: Record<string, any>) {
return request.post('shop/good/rank', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/good/rank', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -440,7 +407,7 @@ export function addGoodRank(params: Record<string, any>) {
export function editGoodRank(params: Record<string, any>) {
return request.put(`shop/good/rank/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -452,7 +419,7 @@ export function editGoodRank(params: Record<string, any>) {
export function editRankStatus(params: Record<string, any>) {
return request.put(`shop/goods/rank/status`, params, {
showErrorMessage: true,
showSuccessMessage: true,
showSuccessMessage: true
})
}
@ -471,9 +438,7 @@ export function deleteGoodRank(id: number) {
* @returns
*/
export function batchDelete(params: Record<string, any>) {
return request.put(`shop/good/rank/batchDelete`, params, {
showSuccessMessage: true,
})
return request.put(`shop/good/rank/batchDelete`, params, { showSuccessMessage: true })
}
/**
@ -482,9 +447,7 @@ export function batchDelete(params: Record<string, any>) {
* @returns
*/
export function modifyGoodsRankSort(params: Record<string, any>) {
return request.put(`shop/good/rank/sort`, params, {
showSuccessMessage: true,
})
return request.put(`shop/good/rank/sort`, params, { showSuccessMessage: true })
}
/**
@ -539,7 +502,7 @@ export function editManjian(params: Record<string, any>) {
* @returns
*/
export function getManjianInfo(params: Record<string, any>) {
return request.get(`shop/manjian/init`, { params })
return request.get(`shop/manjian/init`, { params });
}
/**
@ -566,14 +529,10 @@ export function getManjianMemberPageList(params: Record<string, any>) {
* @returns
*/
export function closeManjian(manjian_id: number) {
return request.put(
`shop/manjian/close/${manjian_id}`,
{},
{
return request.put(`shop/manjian/close/${ manjian_id }`, {}, {
showErrorMessage: true,
showSuccessMessage: true,
}
)
showSuccessMessage: true
})
}
/**
@ -609,9 +568,7 @@ export function getGoodsSkuInfo(params: Record<string, any>) {
* @returns
*/
export function deleteManjian(manjian_id: number) {
return request.delete(`shop/manjian/${manjian_id}`, {
showSuccessMessage: true,
})
return request.delete(`shop/manjian/${ manjian_id }`, { showSuccessMessage: true })
}
/**
@ -620,9 +577,7 @@ export function deleteManjian(manjian_id: number) {
* @returns
*/
export function batchDeleteManjian(params: Record<string, any>) {
return request.put(`shop/manjian/goods/batchDelete`, params, {
showSuccessMessage: true,
})
return request.put(`shop/manjian/goods/batchDelete`, params, { showSuccessMessage: true })
}
/**
@ -631,7 +586,5 @@ export function batchDeleteManjian(params: Record<string, any>) {
* @returns
*/
export function batchCloseMajian(params: Record<string, any>) {
return request.put(`shop/manjian/goods/batchClose`, params, {
showSuccessMessage: true,
})
return request.put(`shop/manjian/goods/batchClose`, params, { showSuccessMessage: true })
}

23
admin/src/addon/shop/api/order.ts

@ -125,10 +125,7 @@ export function orderRefundDetail(refund_id: number) {
* @return
*/
export function auditRefund(params: Record<string, any>) {
return request.put(
`shop/order/refund/audit/${params.order_refund_no}`,
params
)
return request.put(`shop/order/refund/audit/${ params.order_refund_no }`, params)
}
/**
@ -136,10 +133,7 @@ export function auditRefund(params: Record<string, any>) {
* @return
*/
export function refundDelivery(params: Record<string, any>) {
return request.put(
`shop/order/refund/delivery/${params.order_refund_no}`,
params
)
return request.put(`shop/order/refund/delivery/${ params.order_refund_no }`, params)
}
/**
@ -153,9 +147,7 @@ export function getRefundMoney(params: Record<string, any>) {
* 退
*/
export function shopActiveRefund(params: Record<string, any>) {
return request.post(`shop/order/refund/active`, params, {
showSuccessMessage: true,
})
return request.post(`shop/order/refund/active`, params, { showSuccessMessage: true })
}
/**
@ -198,9 +190,7 @@ export function getOrderFrom() {
* @return
*/
export function orderEditPrice(params: Record<string, any>) {
return request.put(`shop/order/edit_price`, params, {
showSuccessMessage: true,
})
return request.put(`shop/order/edit_price`, params, { showSuccessMessage: true })
}
/**
@ -240,10 +230,7 @@ export function getOrderBatchDeliveryList(params: Record<string, any>) {
* @return
*/
export function addBatchOrderDelivery(params: Record<string, any>) {
return request.put(
`shop/order_batch_delivery/add_batch_order_delivery`,
params
)
return request.put(`shop/order_batch_delivery/add_batch_order_delivery`, params)
}
/**

21
admin/src/addon/shop/api/shop_address.ts

@ -15,7 +15,7 @@ export function getShopAddressList(params: Record<string, any>) {
* @returns
*/
export function getShopAddressInfo(id: number) {
return request.get(`shop/shop_address/${id}`)
return request.get(`shop/shop_address/${ id }`);
}
/**
@ -24,10 +24,7 @@ export function getShopAddressInfo(id: number) {
* @returns
*/
export function addShopAddress(params: Record<string, any>) {
return request.post('shop/shop_address', params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.post('shop/shop_address', params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -36,10 +33,7 @@ export function addShopAddress(params: Record<string, any>) {
* @returns
*/
export function editShopAddress(params: Record<string, any>) {
return request.put(`shop/shop_address/${params.id}`, params, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.put(`shop/shop_address/${ params.id }`, params, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -48,10 +42,7 @@ export function editShopAddress(params: Record<string, any>) {
* @returns
*/
export function deleteShopAddress(id: number) {
return request.delete(`shop/shop_address/${id}`, {
showErrorMessage: true,
showSuccessMessage: true,
})
return request.delete(`shop/shop_address/${ id }`, { showErrorMessage: true, showSuccessMessage: true })
}
/**
@ -59,7 +50,7 @@ export function deleteShopAddress(id: number) {
* @returns
*/
export function getShopDefaultDeliveryAddressInfo() {
return request.get('shop/shop_address/default/delivery')
return request.get('shop/shop_address/default/delivery');
}
/**
@ -67,5 +58,5 @@ export function getShopDefaultDeliveryAddressInfo() {
* @returns
*/
export function getOrderRefundAddress() {
return request.get('shop/order/refund/address')
return request.get('shop/order/refund/address');
}

1
admin/src/addon/shop/lang/zh-cn/delivery.company_edit.json

@ -40,4 +40,5 @@
"printStyleTips1": "主流快递单打印纸尺寸一般为:76*130,100*180(单位mm)",
"printStyleTips2": "不填写则取物流公司的默认模板",
"examine": "点击查看"
}

1
admin/src/addon/shop/lang/zh-cn/goods.list.json

@ -105,6 +105,7 @@
"discountHint": "会员折扣说明:按照默认会员等级折扣优惠",
"fixedPriceHint": "会员价说明:指定优惠价格,商品未参与活动时,按照会员价优惠,若商品参与活动,则以活动价为准",
"addGoodsLabel": "添加商品标签",
"addGoodsService": "添加商品服务",
"addGoodsCategory": "添加分类",

2
admin/src/addon/shop/lang/zh-cn/goods.virtual_edit.json

@ -124,6 +124,7 @@
"specValueNameRepeat": "规格值不能重复",
"lackDefaultSpec": "商品缺少默认规格",
"setDeliverGoods": "发货设置",
"autoDeliverGoods": "自动发货",
"handDeliverGoods": "手动发货",
@ -162,4 +163,5 @@
"fixedPriceHint": "会员价说明:指定优惠价格,商品未参与活动时,按照会员价优惠,若商品参与活动,则以活动价为准",
"participateInActiveDisableTips": "商品正在参与营销活动,禁止操作"
}

1
admin/src/addon/shop/lang/zh-cn/marketing.exchange.goods_edit.json

@ -40,6 +40,7 @@
"moneyTipsTwo":"价格不可小于0",
"limitRules":"每人每单可兑换件数",
"couponSelect":"选择优惠券",
"couponSelectPlaceholder":"请选择优惠券",
"couponName":"优惠券名称",

244
admin/src/addon/shop/views/address/edit.vue

@ -4,140 +4,55 @@
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
<el-form
:model="formData"
label-width="90px"
ref="formRef"
:rules="formRules"
class="page-form"
>
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('addressType')" prop="address_type">
<div class="flex flex-col">
<div>
<el-checkbox
v-model="formData.is_delivery_address"
:label="t('deliveryAddress')"
:true-label="1"
:false-label="0"
/>
<el-checkbox
v-model="formData.is_default_delivery"
:label="t('defaultDeliveryAddress')"
:true-label="1"
:false-label="0"
v-show="formData.is_delivery_address"
/>
<el-checkbox v-model="formData.is_delivery_address" :label="t('deliveryAddress')" :true-label="1" :false-label="0"/>
<el-checkbox v-model="formData.is_default_delivery" :label="t('defaultDeliveryAddress')" :true-label="1" :false-label="0" v-show="formData.is_delivery_address"/>
</div>
<div>
<el-checkbox
v-model="formData.is_refund_address"
:label="t('refundAddress')"
:true-label="1"
:false-label="0"
/>
<el-checkbox
v-model="formData.is_default_refund"
:label="t('defaultRefundAddress')"
:true-label="1"
:false-label="0"
v-show="formData.is_refund_address"
/>
<el-checkbox v-model="formData.is_refund_address" :label="t('refundAddress')" :true-label="1" :false-label="0"/>
<el-checkbox v-model="formData.is_default_refund" :label="t('defaultRefundAddress')" :true-label="1" :false-label="0" v-show="formData.is_refund_address"/>
</div>
</div>
</el-form-item>
<el-form-item :label="t('contactName')" prop="contact_name">
<el-input
v-model.trim="formData.contact_name"
clearable
:placeholder="t('contactNamePlaceholder')"
class="input-width"
maxlength="10"
/>
<el-input v-model.trim="formData.contact_name" clearable :placeholder="t('contactNamePlaceholder')" class="input-width" maxlength="10" />
</el-form-item>
<el-form-item :label="t('mobile')" prop="mobile">
<el-input
v-model.trim="formData.mobile"
clearable
:placeholder="t('mobilePlaceholder')"
class="input-width"
@keyup="filterNumber($event)"
@blur="formData.mobile = $event.target.value"
/>
<el-input v-model.trim="formData.mobile" clearable :placeholder="t('mobilePlaceholder')" class="input-width" @keyup="filterNumber($event)" @blur="formData.mobile = $event.target.value"/>
</el-form-item>
<el-form-item :label="t('fullAddress')" prop="address_area">
<el-select
v-model="formData.province_id"
value-key="id"
clearable
class="w-[200px]"
ref="provinceRef"
>
<el-select v-model="formData.province_id" value-key="id" clearable class="w-[200px]" ref="provinceRef">
<el-option :label="t('provincePlaceholder')" :value="0"/>
<el-option
v-for="(item, index) in areaList.province"
:key="index"
:label="item.name"
:value="item.id"
/>
<el-option v-for="(item, index) in areaList.province" :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select
v-model="formData.city_id"
value-key="id"
clearable
class="w-[200px] ml-3"
ref="cityRef"
>
<el-select v-model="formData.city_id" value-key="id" clearable class="w-[200px] ml-3" ref="cityRef">
<el-option :label="t('cityPlaceholder')" :value="0"/>
<el-option
v-for="(item, index) in areaList.city"
:key="index"
:label="item.name"
:value="item.id"
/>
<el-option v-for="(item, index) in areaList.city " :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select
v-model="formData.district_id"
value-key="id"
clearable
class="w-[200px] ml-3"
ref="districtRef"
>
<el-select v-model="formData.district_id" value-key="id" clearable class="w-[200px] ml-3" ref="districtRef">
<el-option :label="t('districtPlaceholder')" :value="0"/>
<el-option
v-for="(item, index) in areaList.district"
:key="index"
:label="item.name"
:value="item.id"
/>
<el-option v-for="(item, index) in areaList.district " :key="index" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item prop="address">
<el-input
v-model.trim="formData.address"
clearable
:placeholder="t('addressPlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.address" clearable :placeholder="t('addressPlaceholder')" class="input-width"/>
</el-form-item>
<el-form-item>
<div
id="container"
class="w-[800px] h-[520px] relative"
v-loading="mapLoading"
></div>
<div id="container" class="w-[800px] h-[520px] relative" v-loading="mapLoading"></div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer !z-[9999]">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
@ -148,11 +63,7 @@
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
getShopAddressInfo,
addShopAddress,
editShopAddress,
} from '@/addon/shop/api/shop_address'
import { getShopAddressInfo, addShopAddress, editShopAddress } from '@/addon/shop/api/shop_address'
import { getMap, getAreaListByPid, getAreaByCode } from '@/app/api/sys'
import { useRoute } from 'vue-router'
import { createMarker, latLngToAddress, addressToLatLng } from '@/utils/qqmap'
@ -163,14 +74,14 @@ const id: number = parseInt(route.query.id as string)
const loading = ref(false)
const pageName = route.meta.title
interface areaType{
province: any[]
city: any[]
province: any[],
city: any[],
district: any[]
}
const areaList = reactive<areaType>({
province: [],
city: [],
district: [],
district: []
})
const provinceRef = ref()
const cityRef = ref()
@ -179,19 +90,17 @@ const districtRef = ref()
/**
* 获取省
*/
getAreaListByPid(0).then((res) => {
getAreaListByPid(0).then(res => {
areaList.province = res.data
})
let mapKey: string = ''
onMounted(() => {
const mapScript = document.createElement('script')
getMap().then((res) => {
getMap().then(res => {
mapKey = res.data.key
mapScript.type = 'text/javascript'
mapScript.src =
'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' +
res.data.key
mapScript.src = 'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' + res.data.key
document.body.appendChild(mapScript)
})
mapScript.onload = () => {
@ -214,7 +123,7 @@ const initMap = () => {
map = new TMap.Map('container', {
center,
zoom: 14,
zoom: 14
})
map.on('tilesloaded', () => {
@ -227,7 +136,7 @@ const initMap = () => {
map.setCenter(evt.latLng)
marker.updateGeometries({
id: 'center',
position: evt.latLng,
position: evt.latLng
})
latLngChange(evt.latLng.lat, evt.latLng.lng)
})
@ -238,12 +147,11 @@ const initMap = () => {
const storeArea = reactive({
province_id: 0,
city_id: 0,
district_id: 0,
district_id: 0
})
const latLngChange = (lat: number, lng: number) => {
latLngToAddress({ mapKey, lat, lng })
.then(({ message, result }) => {
latLngToAddress({ mapKey, lat, lng }).then(({ message, result }) => {
if (message == 'query ok' || message == 'Success') {
formData.lat = result.location.lat
formData.lng = result.location.lng
@ -257,8 +165,7 @@ const latLngChange = (lat: number, lng: number) => {
} else {
console.error(message, result)
}
})
.catch((err) => {
}).catch(err => {
console.log(err)
})
}
@ -276,11 +183,11 @@ const initialFormData = {
address: '',
full_address: '',
lat: 39.908626,
lng: 116.39719,
lng: 116.397190,
is_delivery_address: 0,
is_refund_address: 0,
is_default_delivery: 0,
is_default_refund: 0,
is_default_refund: 0
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -308,11 +215,11 @@ const formRules = computed(() => {
callback(new Error(t('addressTypeRequire')))
}
callback()
},
},
}
}
],
contact_name: [
{ required: true, message: t('contactNamePlaceholder'), trigger: 'blur' },
{ required: true, message: t('contactNamePlaceholder'), trigger: 'blur' }
],
mobile: [
{ required: true, message: t('mobilePlaceholder'), trigger: 'blur' },
@ -323,8 +230,8 @@ const formRules = computed(() => {
callback(new Error(t('mobileTips')))
}
callback()
},
},
}
}
],
address_area: [
{
@ -339,23 +246,21 @@ const formRules = computed(() => {
callback(new Error(t('districtPlaceholder')))
}
callback()
},
},
}
}
],
address: [
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' }
]
}
})
/**
* 获取市
*/
watch(
() => formData.province_id,
(nval) => {
watch(() => formData.province_id, (nval) => {
if (nval) {
getAreaListByPid(formData.province_id).then((res) => {
getAreaListByPid(formData.province_id).then(res => {
areaList.city = res.data
const cityId = formData.city_id
@ -378,17 +283,14 @@ watch(
} else {
formData.city_id = 0
}
}
)
})
/**
* 获取区
*/
watch(
() => formData.city_id,
(nval) => {
watch(() => formData.city_id, (nval) => {
if (nval) {
getAreaListByPid(formData.city_id).then((res) => {
getAreaListByPid(formData.city_id).then(res => {
areaList.district = res.data
const districtId = formData.district_id
@ -411,91 +313,73 @@ watch(
} else {
formData.district_id = 0
}
}
)
})
watch(
() => formData.district_id,
(nval) => {
watch(() => formData.district_id, (nval) => {
if (nval) {
areaChange()
}
}
)
})
const areaChange = debounce(() => {
setTimeout(() => {
const address = [
formData.province_id ? provinceRef.value.selectedLabel : '',
formData.city_id ? cityRef.value.selectedLabel : '',
formData.district_id ? districtRef.value.selectedLabel : '',
formData.district_id ? districtRef.value.selectedLabel : ''
]
addressToLatLng({ mapKey, address: address.join('') }).then(
({ message, result }) => {
addressToLatLng({ mapKey, address: address.join('') }).then(({ message, result }) => {
if (message == 'Success' || message == 'query ok') {
const latLng = new (window as any).TMap.LatLng(
result.location.lat,
result.location.lng
)
const latLng = new (window as any).TMap.LatLng(result.location.lat, result.location.lng)
map.setCenter(latLng)
marker.updateGeometries({
id: 'center',
position: latLng,
position: latLng
})
formData.lat = result.location.lat
formData.lng = result.location.lng
} else {
console.error(message, result)
}
}
)
})
}, 500)
}, 500)
/**
* 地图点选获取市
*/
watch(
() => storeArea.province_id,
(nval) => {
watch(() => storeArea.province_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.province_id).then((res) => {
getAreaListByPid(storeArea.province_id).then(res => {
areaList.city = res.data
formData.province_id = storeArea.province_id
formData.city_id = storeArea.city_id
})
}
}
)
})
/**
* 地图点选获取区
*/
watch(
() => storeArea.city_id,
(nval) => {
watch(() => storeArea.city_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.city_id).then((res) => {
getAreaListByPid(storeArea.city_id).then(res => {
areaList.district = res.data
formData.city_id = storeArea.city_id
formData.district_id = storeArea.district_id
})
}
}
)
})
/**
* 地图点选获取区
*/
watch(
() => storeArea.district_id,
(nval) => {
watch(() => storeArea.district_id, (nval) => {
if (nval) {
formData.district_id = storeArea.district_id
}
}
)
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
@ -508,17 +392,15 @@ const onSave = async (formEl: FormInstance | undefined) => {
data.province_id ? provinceRef.value.selectedLabel : '',
data.city_id ? cityRef.value.selectedLabel : '',
data.district_id ? districtRef.value.selectedLabel : '',
data.address,
data.address
]
data.full_address = address.join('')
const save = id ? editShopAddress : addShopAddress
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
history.back()
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}

133
admin/src/addon/shop/views/address/list.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="addEvent">
@ -8,109 +9,56 @@
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="shopAddressTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="shopAddressTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('mobile')" prop="mobile">
<el-input
v-model.trim="shopAddressTable.searchParam.mobile"
:placeholder="t('mobilePlaceholder')"
/>
<el-input v-model.trim="shopAddressTable.searchParam.mobile" :placeholder="t('mobilePlaceholder')"/>
</el-form-item>
<el-form-item :label="t('fullAddress')" prop="full_address">
<el-input
v-model.trim="shopAddressTable.searchParam.full_address"
:placeholder="t('fullAddressPlaceholder')"
/>
<el-input v-model.trim="shopAddressTable.searchParam.full_address" :placeholder="t('fullAddressPlaceholder')"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadShopAddressList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadShopAddressList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="shopAddressTable.data"
size="large"
v-loading="shopAddressTable.loading"
>
<el-table :data="shopAddressTable.data" size="large" v-loading="shopAddressTable.loading">
<template #empty>
<span>{{ !shopAddressTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="contact_name"
:label="t('contactName')"
min-width="120"
/>
<el-table-column prop="contact_name" :label="t('contactName')" min-width="120"/>
<el-table-column prop="mobile" :label="t('mobile')" min-width="120"/>
<el-table-column
prop="full_address"
:label="t('fullAddress')"
min-width="120"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="is_delivery_address"
:label="t('addressType')"
min-width="120"
align="left"
>
<el-table-column prop="full_address" :label="t('fullAddress')" min-width="120" :show-overflow-tooltip="true"/>
<el-table-column prop="is_delivery_address" :label="t('addressType')" min-width="120" align="left">
<template #default="{ row }">
<div v-if="row.is_delivery_address">
{{ t('deliveryAddress') }}
<el-tag size="small" v-if="row.is_default_delivery">{{
t('default')
}}</el-tag>
<el-tag size="small" v-if="row.is_default_delivery">{{ t('default') }}</el-tag>
</div>
<div v-if="row.is_refund_address">
{{ t('refundAddress') }}
<el-tag size="small" v-if="row.is_default_refund">{{
t('default')
}}</el-tag>
<el-tag size="small" v-if="row.is_default_refund">{{ t('default') }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
align="right"
>
<el-table-column :label="t('operation')" fixed="right" min-width="120" align="right">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{
t('delete')
}}</el-button>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="shopAddressTable.page"
v-model:page-size="shopAddressTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="shopAddressTable.total"
@size-change="loadShopAddressList()"
@current-change="loadShopAddressList"
/>
<el-pagination v-model:current-page="shopAddressTable.page" v-model:page-size="shopAddressTable.limit" layout="total, sizes, prev, pager, next, jumper" :total="shopAddressTable.total" @size-change="loadShopAddressList()" @current-change="loadShopAddressList"/>
</div>
</div>
</el-card>
</div>
</template>
@ -118,13 +66,10 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getShopAddressList,
deleteShopAddress,
} from '@/addon/shop/api/shop_address'
import { getShopAddressList, deleteShopAddress } from '@/addon/shop/api/shop_address'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const route = useRoute()
const pageName = route.meta.title
@ -137,8 +82,8 @@ const shopAddressTable = reactive({
data: [],
searchParam: {
mobile: '',
full_address: '',
},
full_address: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -153,23 +98,17 @@ const loadShopAddressList = (page: number = 1) => {
getShopAddressList({
page: shopAddressTable.page,
limit: shopAddressTable.limit,
...shopAddressTable.searchParam,
})
.then((res) => {
...shopAddressTable.searchParam
}).then(res => {
shopAddressTable.loading = false
shopAddressTable.data = res.data.data
shopAddressTable.total = res.data.total
setTablePageStorage(
shopAddressTable.page,
shopAddressTable.limit,
shopAddressTable.searchParam
)
})
.catch(() => {
setTablePageStorage(shopAddressTable.page, shopAddressTable.limit, shopAddressTable.searchParam);
}).catch(() => {
shopAddressTable.loading = false
})
}
loadShopAddressList(getTablePageStorage(shopAddressTable.searchParam).page)
loadShopAddressList(getTablePageStorage(shopAddressTable.searchParam).page);
const router = useRouter()
@ -192,16 +131,17 @@ const editEvent = (data: any) => {
* 删除商家地址库
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('shopAddressDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('shopAddressDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteShopAddress(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteShopAddress(id).then(() => {
loadShopAddressList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -212,4 +152,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

133
admin/src/addon/shop/views/delivery/company.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<div class="detail-head !m-0">
<div class="left" @click="router.push('/shop/order/delivery')">
@ -10,111 +11,53 @@
<span class="adorn">|</span>
<span class="right">{{ pageName }}</span>
</div>
<el-button type="primary" @click="addEvent">{{
t('addCompany')
}}</el-button>
<el-button type="primary" @click="addEvent">{{ t('addCompany') }}</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="companyTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="companyTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('companyName')" prop="company_name">
<el-input
v-model.trim="companyTable.searchParam.company_name"
:placeholder="t('companyNamePlaceholder')"
/>
<el-input v-model.trim="companyTable.searchParam.company_name" :placeholder="t('companyNamePlaceholder')"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadCompanyList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadCompanyList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="companyTable.data"
size="large"
v-loading="companyTable.loading"
>
<el-table :data="companyTable.data" size="large" v-loading="companyTable.loading">
<template #empty>
<span>{{ !companyTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="company_name"
:label="t('companyName')"
min-width="120"
/>
<el-table-column prop="company_name" :label="t('companyName')" min-width="120"/>
<el-table-column prop="logo" :label="t('logo')" min-width="120">
<template #default="{ row }">
<div class="w-[50px] h-[50px] flex items-center justify-center">
<img
v-if="row.logo"
class="max-w-[100%] max-h-[100%]"
:src="img(row.logo)"
/>
<img v-if="row.logo" class="max-w-[100%] max-h-[100%]" :src="img(row.logo)"/>
</div>
</template>
</el-table-column>
<el-table-column prop="url" :label="t('url')" min-width="120"/>
<el-table-column
prop="express_no"
:label="t('expressNo')"
min-width="120"
/>
<el-table-column
prop="express_no_electronic_sheet"
:label="t('expressNoElectronicSheet')"
min-width="120"
/>
<el-table-column
:label="t('electronicSheetSwitchName')"
min-width="120"
>
<el-table-column prop="express_no" :label="t('expressNo')" min-width="120"/>
<el-table-column prop="express_no_electronic_sheet" :label="t('expressNoElectronicSheet')" min-width="120"/>
<el-table-column :label="t('electronicSheetSwitchName')" min-width="120">
<template #default="{ row }">
<span>{{
row.electronic_sheet_switch == 1 ? '支持' : '不支持'
}}</span>
<span>{{row.electronic_sheet_switch == 1 ? '支持' : '不支持'}}</span>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.company_id)"
>{{ t('delete') }}
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.company_id)">{{ t('delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="companyTable.page"
v-model:page-size="companyTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="companyTable.total"
@size-change="loadCompanyList()"
@current-change="loadCompanyList"
/>
<el-pagination v-model:current-page="companyTable.page" v-model:page-size="companyTable.limit" layout="total, sizes, prev, pager, next, jumper" :total="companyTable.total" @size-change="loadCompanyList()" @current-change="loadCompanyList"/>
</div>
</div>
</el-card>
@ -128,7 +71,7 @@ import { getCompanyPageList, deleteCompany } from '@/addon/shop/api/delivery'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const route = useRoute()
const router = useRouter()
@ -145,8 +88,8 @@ const companyTable = reactive({
logo: '',
url: '',
create_time: '',
modify_time: '',
},
modify_time: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -161,24 +104,18 @@ const loadCompanyList = (page: number = 1) => {
getCompanyPageList({
page: companyTable.page,
limit: companyTable.limit,
...companyTable.searchParam,
})
.then((res) => {
...companyTable.searchParam
}).then(res => {
companyTable.loading = false
companyTable.data = res.data.data
companyTable.total = res.data.total
setTablePageStorage(
companyTable.page,
companyTable.limit,
companyTable.searchParam
)
})
.catch(() => {
setTablePageStorage(companyTable.page, companyTable.limit, companyTable.searchParam);
}).catch(() => {
companyTable.loading = false
})
}
loadCompanyList(getTablePageStorage(companyTable.searchParam).page)
loadCompanyList(getTablePageStorage(companyTable.searchParam).page);
/**
* 添加物流公司
@ -199,16 +136,17 @@ const editEvent = (data: any) => {
* 删除物流公司
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('companyDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('companyDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteCompany(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteCompany(id).then(() => {
loadCompanyList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -219,4 +157,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

306
admin/src/addon/shop/views/delivery/company_edit.vue

@ -1,267 +1,112 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header
:content="formData.company_id ? t('updateCompany') : t('addCompany')"
:icon="ArrowLeft"
@back="back()"
/>
<el-page-header :content="formData.company_id ? t('updateCompany') : t('addCompany')" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-form
:model="formData"
label-width="130px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form :model="formData" label-width="130px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('companyName')" prop="company_name">
<el-input
v-model.trim="formData.company_name"
maxlength="20"
clearable
:placeholder="t('companyNamePlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.company_name" maxlength="20" clearable :placeholder="t('companyNamePlaceholder')" class="input-width"/>
</el-form-item>
<el-form-item :label="t('logo')">
<upload-image v-model="formData.logo"/>
</el-form-item>
<el-form-item :label="t('url')">
<el-input
v-model.trim="formData.url"
clearable
:placeholder="t('urlPlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.url" clearable :placeholder="t('urlPlaceholder')" class="input-width"/>
</el-form-item>
<el-form-item :label="t('expressNo')">
<div>
<el-input
v-model.trim="formData.express_no"
clearable
:placeholder="t('expressNoPlaceholder')"
class="input-width"
/>
<p
class="w-[380px] text-[12px] text-[#999] mt-[5px] leading-[20px]"
>
{{ t('expressNoTips') }}
</p>
<el-input v-model.trim="formData.express_no" clearable :placeholder="t('expressNoPlaceholder')" class="input-width"/>
<p class="w-[380px] text-[12px] text-[#999] mt-[5px] leading-[20px]">{{ t('expressNoTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('expressNoElectronicSheet')">
<div>
<el-input
v-model.trim="formData.express_no_electronic_sheet"
clearable
:placeholder="t('expressNoPlaceholder')"
class="input-width"
/>
<p
class="w-[380px] text-[12px] text-[#999] mt-[5px] leading-[20px]"
>
{{ t('expressNoElectronicSheetTips') }}
</p>
<el-input v-model.trim="formData.express_no_electronic_sheet" clearable :placeholder="t('expressNoPlaceholder')" class="input-width"/>
<p class="w-[380px] text-[12px] text-[#999] mt-[5px] leading-[20px]">{{ t('expressNoElectronicSheetTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('electronicSheetSwitch')">
<el-switch
v-model="formData.electronic_sheet_switch"
:active-value="1"
:inactive-value="0"
/>
<el-switch v-model="formData.electronic_sheet_switch" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item
:label="t('expType')"
prop="exp_type"
v-show="formData.electronic_sheet_switch"
>
<el-form-item :label="t('expType')" prop="exp_type" v-show="formData.electronic_sheet_switch">
<div class="w-[600px]">
<el-table
:data="formData.exp_type"
size="large"
v-show="formData.exp_type.length"
>
<el-table :data="formData.exp_type" size="large" v-show="formData.exp_type.length">
<template #empty>
<span>{{
formData.exp_type.length == 0 ? t('emptyData') : ''
}}</span>
<span>{{ formData.exp_type.length == 0 ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="name"
:label="t('expTypeName')"
min-width="200"
>
<el-table-column prop="name" :label="t('expTypeName')" min-width="200">
<template #default="{ row }">
<el-input
v-model.trim="row.text"
class="input-width"
maxlength="20"
clearable
show-word-limit
/>
<el-input v-model.trim="row.text" class="input-width" maxlength="20" clearable show-word-limit />
</template>
</el-table-column>
<el-table-column
prop="name"
:label="t('expTypeValue')"
min-width="120"
>
<el-table-column prop="name" :label="t('expTypeValue')" min-width="120">
<template #default="{ row }">
<el-input
v-model.trim="row.value"
class="!w-[150px]"
maxlength="6"
clearable
show-word-limit
@keyup="filterNumber($event)"
/>
<el-input v-model.trim="row.value" class="!w-[150px]" maxlength="6" clearable show-word-limit @keyup="filterNumber($event)" />
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="60"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="60">
<template #default="{ row,$index }">
<el-button
type="primary"
link
@click="deleteExpTypeValueEvent($index)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="deleteExpTypeValueEvent($index)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<el-button
type="primary"
plain
@click="addExpTypeValueEvent"
:class="{ 'mt-[10px]': formData.exp_type.length }"
v-show="formData.exp_type.length < expTypeMaxLength"
>{{ t('addExpType') }}</el-button
>
<el-button type="primary" plain @click="addExpTypeValueEvent" :class="{'mt-[10px]': formData.exp_type.length}" v-show="formData.exp_type.length < expTypeMaxLength">{{ t('addExpType') }}</el-button>
<div class="text-[12px] text-[#999] mt-[5px] leading-[20px]">
<span>{{ t('expTypeTips') }}</span>
<a
class="ml-[3px] text-[var(--el-color-primary)]"
target="_blank"
href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/hgx758hom5p6wz0l"
>{{ t('examine') }}</a
>
<a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/hgx758hom5p6wz0l">{{t('examine')}}</a>
</div>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">
{{ t('expTypeTips1') }}
</p>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('expTypeTips1') }}</p>
</div>
</el-form-item>
<el-form-item
:label="t('printStyle')"
prop="print_style"
v-show="formData.electronic_sheet_switch"
>
<el-form-item :label="t('printStyle')" prop="print_style" v-show="formData.electronic_sheet_switch">
<div class="w-[600px]">
<el-table
:data="formData.print_style"
size="large"
v-show="formData.print_style.length"
>
<el-table :data="formData.print_style" size="large" v-show="formData.print_style.length">
<template #empty>
<span>{{
formData.print_style.length == 0 ? t('emptyData') : ''
}}</span>
<span>{{ formData.print_style.length == 0 ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="name"
:label="t('printStyleName')"
min-width="200"
>
<el-table-column prop="name" :label="t('printStyleName')" min-width="200">
<template #default="{ row }">
<el-input
v-model.trim="row.template_name"
class="input-width"
maxlength="20"
clearable
show-word-limit
/>
<el-input v-model.trim="row.template_name" class="input-width" maxlength="20" clearable show-word-limit />
</template>
</el-table-column>
<el-table-column
prop="name"
:label="t('printStyleId')"
min-width="120"
>
<el-table-column prop="name" :label="t('printStyleId')" min-width="120">
<template #default="{ row }">
<el-input
v-model.trim="row.template_size"
class="!w-[150px]"
maxlength="6"
clearable
show-word-limit
/>
<el-input v-model.trim="row.template_size" class="!w-[150px]" maxlength="6" clearable show-word-limit />
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="60"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="60">
<template #default="{ row,$index }">
<el-button
type="primary"
link
@click="deletePrintStyleValueEvent($index)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="deletePrintStyleValueEvent($index)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<el-button
type="primary"
plain
@click="addPrintStyleValueEvent"
:class="{ 'mt-[10px]': formData.print_style.length }"
v-show="formData.print_style.length < printStyleMaxLength"
>{{ t('addPrintStyle') }}</el-button
>
<el-button type="primary" plain @click="addPrintStyleValueEvent" :class="{'mt-[10px]': formData.print_style.length}" v-show="formData.print_style.length < printStyleMaxLength">{{ t('addPrintStyle') }}</el-button>
<div class="text-[12px] text-[#999] mt-[5px] leading-[20px]">
<span>{{ t('printStyleTips') }}</span>
<a
class="ml-[3px] text-[var(--el-color-primary)]"
target="_blank"
href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/vpptucr1q5ahcxa7"
>{{ t('examine') }}</a
>
<a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/vpptucr1q5ahcxa7">{{t('examine')}}</a>
</div>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">
{{ t('printStyleTips1') }}
</p>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">
{{ t('printStyleTips2') }}
</p>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('printStyleTips1') }}</p>
<p class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('printStyleTips2') }}</p>
</div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="save(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="save(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('back') }}</el-button>
</div>
</div>
@ -271,11 +116,7 @@
<script lang="ts" setup>
import { reactive, ref, computed } from 'vue'
import { t } from '@/lang'
import {
addCompany,
editCompany,
getCompanyInfo,
} from '@/addon/shop/api/delivery'
import { addCompany, editCompany, getCompanyInfo } from '@/addon/shop/api/delivery'
import { FormInstance } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { filterNumber } from '@/utils/common'
@ -298,10 +139,10 @@ const initialFormData = {
logo: '',
url: '',
express_no: '',
express_no_electronic_sheet: '',
express_no_electronic_sheet: "",
print_style: [],
exp_type: [],
electronic_sheet_switch: 1,
electronic_sheet_switch: 1
}
const printStyleMaxLength = ref(10)
@ -311,9 +152,9 @@ const formData: Record<string, any> = reactive({ ...initialFormData })
formData.company_id = ref(route.query.company_id)
const getCompanyInfoFn = ()=>{
getCompanyInfo(formData.company_id).then((res) => {
loading.value = false
let data = res.data
getCompanyInfo(formData.company_id).then(res => {
loading.value = false;
let data = res.data;
if (data) {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
@ -322,9 +163,9 @@ const getCompanyInfoFn = () => {
})
}
if(formData.company_id){
getCompanyInfoFn()
getCompanyInfoFn();
}else{
loading.value = false
loading.value = false;
}
const formRef = ref<FormInstance>()
@ -333,7 +174,7 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
company_name: [
{ required: true, message: t('companyNamePlaceholder'), trigger: 'blur' },
{ required: true, message: t('companyNamePlaceholder'), trigger: 'blur' }
],
exp_type: [
{
@ -341,25 +182,25 @@ const formRules = computed(() => {
validator: (rule: any, value: any, callback: any) => {
if(!value.length){
callback()
return false
return false;
}
let textArr = [] //
let valArr = [] //
let textArr = []; //
let valArr = []; //
for(let i = 0; i < value.length; i++){
if(!value[i].text){
callback(new Error(t('expTypeTextTips')))
break
break;
}else if(value[i].text){
textArr.push(value[i].text)
textArr.push(value[i].text);
}
if(!value[i].value){
callback(new Error(t('expTypeValueTips')))
break
break;
}else if(parseFloat(value[i].value) == 0 ){
callback(new Error(t('expTypeValueNullTips')))
break
break;
}else if(value[i].value){
valArr.push(value[i].value)
valArr.push(value[i].value);
}
}
if(new Set(textArr).size !== textArr.length){
@ -369,8 +210,8 @@ const formRules = computed(() => {
callback(new Error(t('expTypeValueRepeatTips')))
}
callback()
},
},
}
}
],
print_style: [
{
@ -378,22 +219,22 @@ const formRules = computed(() => {
validator: (rule: any, value: any, callback: any) => {
if(!value.length){
callback()
return false
return false;
}
let nameArr = [] //
let sizeArr = [] //
let nameArr = []; //
let sizeArr = []; //
for(let i = 0; i < value.length; i++){
if(!value[i].template_name){
callback(new Error(t('printStyleNameTips')))
break
break;
}else if(value[i].template_name){
nameArr.push(value[i].template_name)
nameArr.push(value[i].template_name);
}
if(!value[i].template_size){
callback(new Error(t('printStyleSizeTips')))
break
break;
}else if(value[i].template_size){
sizeArr.push(value[i].template_size)
sizeArr.push(value[i].template_size);
}
}
if(new Set(nameArr).size !== nameArr.length){
@ -403,9 +244,9 @@ const formRules = computed(() => {
callback(new Error(t('printStyleSizeRepeatTips')))
}
callback()
},
},
],
}
}
]
}
})
@ -413,7 +254,7 @@ const formRules = computed(() => {
const addPrintStyleValueEvent = ()=>{
formData.print_style.push({
template_name: '',
template_size: '',
template_size: ''
})
}
@ -421,7 +262,7 @@ const addPrintStyleValueEvent = () => {
const addExpTypeValueEvent = ()=>{
formData.exp_type.push({
text: '',
value: '',
value: ''
})
}
@ -445,21 +286,22 @@ const save = async (formEl: FormInstance | undefined) => {
const api = formData.company_id ? editCompany : addCompany
await formEl.validate(async (valid) => {
if (valid) {
repeat.value = true
const data = formData
api(data)
.then((res) => {
api(data).then(res => {
router.push('/shop/order/delivery/company')
repeat.value = false
})
.catch(() => {
}).catch(() => {
repeat.value = false
})
}
})
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

73
admin/src/addon/shop/views/delivery/components/delivery-personnel-edit.vue

@ -1,50 +1,17 @@
<template>
<el-dialog
v-model="showDialog"
:title="
formData.deliver_id ? t('updateDeliver') : t('addDeliveryPersonnel')
"
width="480"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-dialog v-model="showDialog" :title="formData.deliver_id ? t('updateDeliver') : t('addDeliveryPersonnel')" width="480" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('deliverName')" prop="deliver_name">
<el-input
v-model.trim="formData.deliver_name"
clearable
:placeholder="t('deliverNamePlaceholder')"
class="input-width"
maxlength="10"
/>
<el-input v-model.trim="formData.deliver_name" clearable :placeholder="t('deliverNamePlaceholder')" class="input-width" maxlength="10"/>
</el-form-item>
<el-form-item :label="t('deliverMobile')" prop="deliver_mobile">
<el-input
v-model.trim="formData.deliver_mobile"
clearable
:placeholder="t('deliverMobilePlaceholder')"
class="input-width"
@keyup="filterNumber($event)"
@blur="formData.deliver_mobile = $event.target.value"
/>
<el-input v-model.trim="formData.deliver_mobile" clearable :placeholder="t('deliverMobilePlaceholder')" class="input-width" @keyup="filterNumber($event)" @blur="formData.deliver_mobile = $event.target.value" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -54,11 +21,7 @@
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addShopDeliver,
editShopDeliver,
getShopDeliverInfo,
} from '@/addon/shop/api/delivery'
import { addShopDeliver, editShopDeliver, getShopDeliverInfo } from '@/addon/shop/api/delivery'
import { filterNumber } from '@/utils/common'
const showDialog = ref(false)
@ -70,7 +33,7 @@ const loading = ref(false)
const initialFormData = {
deliver_id: '',
deliver_name: '',
deliver_mobile: '',
deliver_mobile: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -80,21 +43,17 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
deliver_name: [
{ required: true, message: t('deliverNamePlaceholder'), trigger: 'blur' },
{ required: true, message: t('deliverNamePlaceholder'), trigger: 'blur' }
],
deliver_mobile: [
{
required: true,
message: t('deliverMobilePlaceholder'),
trigger: 'blur',
},
{ required: true, message: t('deliverMobilePlaceholder'), trigger: 'blur' },
{ min: 11, max: 11, message: '请输入11位手机号码', trigger: 'blur' },
{
pattern :/^1[23456789]\d{9}$/,
message: '请输入正确的手机号码',
trigger: 'blur',
},
],
trigger: 'blur'
}
]
}
})
@ -114,13 +73,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
const data = formData
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
@ -143,7 +100,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>

94
admin/src/addon/shop/views/delivery/config.vue

@ -3,12 +3,7 @@
<div class="flex ml-[18px] justify-between items-center mt-[20px]">
<span class="text-page-title">{{ pageName }}</span>
</div>
<div
class="p-[18px] logistics-body"
ref="tableRef"
:key="toggleIndex"
v-if="!loading"
>
<div class="p-[18px] logistics-body" ref="tableRef" :key="toggleIndex" v-if="!loading">
<template v-for="(item, index) in tableData" :key="item.key">
<div class="mb-[20px] bg-[#fff]">
<el-card shadow="never">
@ -16,76 +11,29 @@
<div class="flex items-center justify-between">
<div class="flex items-center">
<i class="iconfont icontuodong vues-rank mr-[5px]"></i>
<el-input
v-focus
v-if="index === activeIndex"
v-model.trim="inputValue"
class="w-[120px]"
maxlength="10"
@blur="inputBlur"
/>
<span v-else class="font-600 text-[14px]">{{
item.name
}}</span>
<el-icon
class="text-color ml-[10px] cursor-pointer"
@click="edit(index)"
>
<el-input v-focus v-if="index === activeIndex" v-model.trim="inputValue" class="w-[120px]" maxlength="10" @blur="inputBlur"/>
<span v-else class="font-600 text-[14px]">{{ item.name }}</span>
<el-icon class="text-color ml-[10px] cursor-pointer" @click="edit(index)">
<EditPen/>
</el-icon>
</div>
<el-switch
v-model="item.status"
active-value="1"
inactive-value="2"
@change="update(item)"
/>
<el-switch v-model="item.status" active-value="1" inactive-value="2" @change="update(item)"/>
</div>
</template>
<div class="flex items-center justify-between">
<span class="text-[#666666] text-[14px]">{{ t(item.key) }}</span>
<div>
<template v-if="item.key === 'local_delivery'">
<el-button
type="primary"
link
@click="goRouter('/shop/order/delivery/staff')"
>{{ t('deliveryStaff') }}</el-button
>
<el-button
type="primary"
link
@click="goRouter('/shop/order/delivery/local')"
>{{ t('localConfig') }}</el-button
>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/staff')">{{ t('deliveryStaff') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/local')">{{ t('localConfig') }}</el-button>
</template>
<template v-if="item.key === 'express'">
<el-button
type="primary"
link
@click="goRouter('/shop/order/delivery/company')"
>{{ t('deliveryCompany') }}</el-button
>
<el-button
type="primary"
link
@click="goRouter('/shop/order/shipping/template')"
>{{ t('deliveryTemplate') }}</el-button
>
<el-button
type="primary"
link
@click="goRouter('/shop/order/delivery/search')"
>{{ t('deliverySearch') }}</el-button
>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/company')">{{ t('deliveryCompany') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/shipping/template')">{{ t('deliveryTemplate') }}</el-button>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/search')">{{ t('deliverySearch') }}</el-button>
</template>
<template v-if="item.key === 'store'">
<el-button
type="primary"
link
@click="goRouter('/shop/order/delivery/store')"
>{{ t('deliveryStore') }}</el-button
>
<el-button type="primary" link @click="goRouter('/shop/order/delivery/store')">{{ t('deliveryStore') }}</el-button>
</template>
</div>
</div>
@ -99,10 +47,7 @@
import { onMounted, nextTick, ref, toRaw } from 'vue'
import { t } from '@/lang'
import Sortable from 'sortablejs'
import {
getShopDeliveryList,
setShopDeliveryConfig,
} from '@/addon/shop/api/delivery'
import { getShopDeliveryList, setShopDeliveryConfig } from '@/addon/shop/api/delivery'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
@ -117,15 +62,13 @@ interface TableDataType {
const tableData = ref<TableDataType[]>([])
const getShopDeliveryListFn = () => {
loading.value = true
getShopDeliveryList()
.then((res) => {
getShopDeliveryList().then(res => {
tableData.value = res.data
loading.value = false
nextTick(() => {
if(rowDrop) rowDrop()
})
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
@ -147,7 +90,7 @@ const rowDrop = () => {
rowDrop()
})
update()
},
}
})
}
@ -159,10 +102,7 @@ const edit = (index: number) => {
inputValue.value = toRaw(tableData.value[index].name)
}
const inputBlur = () => {
if (
inputValue.value == '' ||
tableData.value[activeIndex.value].name === inputValue.value
) {
if (inputValue.value == '' || tableData.value[activeIndex.value].name === inputValue.value) {
activeIndex.value = null
inputValue.value = ''
return false
@ -173,7 +113,7 @@ const inputBlur = () => {
}
const update = () => {
setShopDeliveryConfig({
value: tableData.value,
value: tableData.value
})
}
const goRouter = (path: string) => {

172
admin/src/addon/shop/views/delivery/electronic_sheet.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center mb-[5px]">
<span class="text-lg">{{pageName}}</span>
<el-button type="primary" @click="addEvent">
@ -8,152 +9,72 @@
</el-button>
</div>
<el-tabs
model-value="/shop/delivery/electronic_sheet"
@tab-change="handleClick"
>
<el-tab-pane
:label="t('tabESTemplate')"
name="/shop/delivery/electronic_sheet"
/>
<el-tab-pane
:label="t('tabESConfig')"
name="/shop/delivery/electronic_sheet/config"
/>
<el-tabs model-value="/shop/delivery/electronic_sheet" @tab-change="handleClick">
<el-tab-pane :label="t('tabESTemplate')" name="/shop/delivery/electronic_sheet" />
<el-tab-pane :label="t('tabESConfig')" name="/shop/delivery/electronic_sheet/config" />
</el-tabs>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="tableData.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('templateName')" prop="template_name">
<el-input
v-model.trim="tableData.searchParam.template_name"
:placeholder="t('templateNamePlaceholder')"
maxlength="30"
/>
<el-input v-model.trim="tableData.searchParam.template_name" :placeholder="t('templateNamePlaceholder')" maxlength="30" />
</el-form-item>
<el-form-item :label="t('expressCompany')" prop="express_company_id">
<el-select
v-model="tableData.searchParam.express_company_id"
:placeholder="t('expressCompanyPlaceholder')"
clearable
>
<el-option
v-for="item in companyList"
:key="item.company_id"
:label="item.company_name"
:value="item.company_id"
/>
<el-select v-model="tableData.searchParam.express_company_id" :placeholder="t('expressCompanyPlaceholder')" clearable>
<el-option v-for="item in companyList" :key="item.company_id" :label="item.company_name" :value="item.company_id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="tableData.data"
size="large"
v-loading="tableData.loading"
>
<el-table :data="tableData.data" size="large" v-loading="tableData.loading">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
:label="t('templateName')"
min-width="200"
:show-overflow-tooltip="true"
>
<el-table-column :label="t('templateName')" min-width="200" :show-overflow-tooltip="true">
<template #default="{ row }">
<el-tag size="small" v-if="row.is_default">{{
t('isDefault')
}}</el-tag>
<el-tag size="small" v-if="row.is_default">{{ t('isDefault') }}</el-tag>
<span class="ml-[8px]">{{row.template_name}}</span>
</template>
</el-table-column>
<el-table-column
prop="express_company_id"
:label="t('expressCompany')"
min-width="120"
:show-overflow-tooltip="true"
>
<el-table-column prop="express_company_id" :label="t('expressCompany')" min-width="120" :show-overflow-tooltip="true">
<template #default="{ row }">
<div>{{ row.company.company_name }}</div>
</template>
</el-table-column>
<el-table-column
prop="pay_type_name"
:label="t('payType')"
min-width="80"
:show-overflow-tooltip="true"
/>
<el-table-column prop="pay_type_name" :label="t('payType')" min-width="80" :show-overflow-tooltip="true"/>
<el-table-column
prop="status"
:label="t('status')"
min-width="80"
:show-overflow-tooltip="true"
>
<el-table-column prop="status" :label="t('status')" min-width="80" :show-overflow-tooltip="true" >
<template #default="{ row }">
<div v-if="row.status == 1">{{ t('statusOn') }}</div>
<div v-if="row.status == 0">{{ t('statusOff') }}</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="80"
align="right"
>
<el-table-column :label="t('operation')" fixed="right" min-width="80" align="right">
<template #default="{ row }">
<el-button
type="primary"
link
v-if="!row.is_default"
@click="setDefaultEvent(row.id)"
>{{ t('setDefault') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
v-if="!row.is_default"
@click="deleteEvent(row.id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link v-if="!row.is_default" @click="setDefaultEvent(row.id)">{{ t('setDefault') }}</el-button>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link v-if="!row.is_default" @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="loadList()"
@current-change="loadList"
/>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
@size-change="loadList()" @current-change="loadList" />
</div>
</div>
</el-card>
</div>
</template>
@ -161,18 +82,14 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getElectronicSheetPageList,
deleteElectronicSheet,
setDefaultElectronicSheet,
} from '@/addon/shop/api/electronic_sheet'
import { getElectronicSheetPageList, deleteElectronicSheet, setDefaultElectronicSheet } from '@/addon/shop/api/electronic_sheet'
import { ElMessageBox,FormInstance } from 'element-plus'
import { useRoute,useRouter } from 'vue-router'
import { getCompanyList } from '@/addon/shop/api/delivery'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const pageName = route.meta.title;
const tableData = reactive({
page: 1,
@ -181,9 +98,9 @@ const tableData = reactive({
loading: true,
data: [],
searchParam: {
template_name: '',
express_company_id: '',
},
template_name: "",
express_company_id: "",
}
})
const searchFormRef = ref<FormInstance>()
@ -202,14 +119,12 @@ const loadList = (page: number = 1) => {
getElectronicSheetPageList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
})
.then((res) => {
...tableData.searchParam
}).then(res => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
})
.catch(() => {
}).catch(() => {
tableData.loading = false
})
}
@ -219,9 +134,9 @@ loadList()
const companyList = ref([])
getCompanyList({
electronic_sheet_switch: 1,
electronic_sheet_switch: 1
}).then((res:any)=>{
companyList.value = res.data
companyList.value = res.data;
})
/**
@ -243,11 +158,13 @@ const editEvent = (data: any) => {
* 删除电子面单
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('electronicSheetDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('electronicSheetDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
}
).then(() => {
deleteElectronicSheet(id).then(() => {
loadList()
})
@ -257,11 +174,13 @@ const deleteEvent = (id: number) => {
* 设置默认电子面单模版
*/
const setDefaultEvent = (id: number) => {
ElMessageBox.confirm(t('electronicSheetSetDefaultTips'), t('warning'), {
ElMessageBox.confirm(t('electronicSheetSetDefaultTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
}
).then(() => {
setDefaultElectronicSheet({id}).then(() => {
loadList()
})
@ -275,4 +194,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

139
admin/src/addon/shop/views/delivery/electronic_sheet_config.vue

@ -1,32 +1,17 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center mb-[5px] h-[32px]">
<span class="text-lg">{{pageName}}</span>
</div>
<el-tabs
model-value="/shop/delivery/electronic_sheet/config"
@tab-change="handleClick"
>
<el-tab-pane
:label="t('tabESTemplate')"
name="/shop/delivery/electronic_sheet"
/>
<el-tab-pane
:label="t('tabESConfig')"
name="/shop/delivery/electronic_sheet/config"
/>
<el-tabs model-value="/shop/delivery/electronic_sheet/config" @tab-change="handleClick">
<el-tab-pane :label="t('tabESTemplate')" name="/shop/delivery/electronic_sheet" />
<el-tab-pane :label="t('tabESConfig')" name="/shop/delivery/electronic_sheet/config" />
</el-tabs>
<el-form
class="page-form"
:model="formData"
:rules="formRules"
label-width="150px"
ref="formRef"
v-loading="loading"
>
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('apiSet') }}</h3>
@ -37,47 +22,29 @@
</el-radio-group>
<template v-if="formData.interface_type == 'kdbird'">
<p class="text-[12px] text-[#b2b2b2]">
{{ t('promptTips1-1')
}}<el-button
class="button-size"
type="primary"
link
@click="kdnEvent('https://www.kdniao.com')"
>https://www.kdniao.com</el-button
>
{{ t('promptTips1-1') }}<el-button class="button-size" type="primary" link @click="kdnEvent('https://www.kdniao.com')">https://www.kdniao.com</el-button>
</p>
</template>
</div>
</el-form-item>
<div v-if="formData.interface_type == 'kdbird'">
<el-form-item :label="t('kdnEBusinessIDLabel')" class="input-item">
<div>
<el-input
v-model.trim="formData.kdniao_id"
:placeholder="t('kdnEBusinessIDPlaceholder')"
class="input-width"
clearable
/>
<p class="text-[12px] text-[#b2b2b2]">
{{ t('kdnEBusinessIDTips') }}
</p>
<el-input v-model.trim="formData.kdniao_id" :placeholder="t('kdnEBusinessIDPlaceholder')" class="input-width" clearable />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kdnEBusinessIDTips') }}</p>
</div>
</el-form-item>
<el-form-item label="API key" class="input-item">
<div>
<el-input
v-model.trim="formData.kdniao_api_key"
clearable
:placeholder="t('kdnAppKeyPlaceholder')"
class="input-width"
/>
<p class="text-[12px] text-[#b2b2b2]">
{{ t('kdnAppKeyTips') }}
</p>
<el-input v-model.trim="formData.kdniao_api_key" clearable :placeholder="t('kdnAppKeyPlaceholder')" class="input-width" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kdnAppKeyTips') }}</p>
</div>
</el-form-item>
</div>
</el-card>
<el-card class="box-card !border-none" shadow="never">
@ -85,78 +52,42 @@
<el-alert type="warning" :closable="false" class="!mb-[10px]">
<template #default>
<p>
用双端口加载主JS文件Lodop.js或CLodopfuncs.js兼容老版本以防其中某端口被占
</p>
<p>用双端口加载主JS文件Lodop.js或CLodopfuncs.js兼容老版本以防其中某端口被占</p>
<p>HTTP推荐端口8000/18000HTTPS推荐端口8443</p>
<p>1. 请将打印机连接至本机 </p>
<p>
2. 在本机上安装打印控件下载链接<a
href="http://www.lodop.net/download.html"
target="_blank"
class="text-primary"
>http://www.lodop.net/download.html</a
>
</p>
<p>2. 在本机上安装打印控件下载链接<a href="http://www.lodop.net/download.html" target="_blank" class="text-primary">http://www.lodop.net/download.html</a></p>
<p>3. 将打印控件中的打印端口下面的打印端口设为相同</p>
</template>
</el-alert>
<el-form-item
:label="t('serverPort1')"
class="input-item-required"
prop="server_port1"
>
<el-form-item :label="t('serverPort1')" class="input-item-required" prop="server_port1">
<div>
<el-input
v-model.trim="formData.server_port1"
:placeholder="t('serverPort1Placeholder')"
class="input-width"
clearable
/>
<el-input v-model.trim="formData.server_port1" :placeholder="t('serverPort1Placeholder')" class="input-width" clearable />
</div>
</el-form-item>
<el-form-item
:label="t('serverPort2')"
class="input-item-required"
prop="server_port2"
>
<el-form-item :label="t('serverPort2')" class="input-item-required" prop="server_port2">
<div>
<el-input
v-model.trim="formData.server_port2"
:placeholder="t('serverPort2Placeholder')"
class="input-width"
clearable
/>
<el-input v-model.trim="formData.server_port2" :placeholder="t('serverPort2Placeholder')" class="input-width" clearable />
</div>
</el-form-item>
<el-form-item
:label="t('httpsPort')"
class="input-item-required"
prop="https_port"
>
<el-form-item :label="t('httpsPort')" class="input-item-required" prop="https_port">
<div>
<el-input
v-model.trim="formData.https_port"
:placeholder="t('httpsPortPlaceholder')"
class="input-width"
clearable
/>
<el-input v-model.trim="formData.https_port" :placeholder="t('httpsPortPlaceholder')" class="input-width" clearable />
</div>
</el-form-item>
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="loading" @click="save(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
</div>
</div>
</el-card>
</div>
</template>
@ -165,14 +96,11 @@ import { reactive, ref } from 'vue'
import { t } from '@/lang'
import { FormInstance, FormRules } from 'element-plus'
import { useRoute,useRouter } from 'vue-router'
import {
setElectronicSheetConfig,
getElectronicSheetConfig,
} from '@/addon/shop/api/electronic_sheet'
import { setElectronicSheetConfig, getElectronicSheetConfig } from '@/addon/shop/api/electronic_sheet'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const pageName = route.meta.title;
const loading = ref(true)
const handleClick = (path: string) => {
@ -185,7 +113,7 @@ const formData: any = reactive({
kdniao_api_key: '',
server_port1: '8000',
server_port2: '18000',
https_port: '8443',
https_port: '8443'
})
const setFormData = async () => {
@ -226,25 +154,24 @@ const save = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
setElectronicSheetConfig(formData)
.then(() => {
setElectronicSheetConfig(formData).then(() => {
loading.value = false
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
})
}
</script>
<style lang="scss" scoped>
.input-item {
margin-bottom: 10px !important;
margin-bottom: 10px !important
}
.input-item-required {
margin-bottom: 20px !important;
margin-bottom: 20px !important
}
.button-size {
@ -252,6 +179,6 @@ const save = async (formEl: FormInstance | undefined) => {
}
.el-radio.el-radio--large {
height: auto !important;
height: auto !important
}
</style>

220
admin/src/addon/shop/views/delivery/electronic_sheet_edit.vue

@ -4,179 +4,85 @@
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back" />
</el-card>
<el-form
class="page-form"
:model="formData"
:rules="formRules"
label-width="150px"
ref="formRef"
v-loading="loading"
>
<el-form class="page-form" :model="formData" :rules="formRules" label-width="150px" ref="formRef" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('basicSettings') }}</h3>
<el-form-item :label="t('templateName')" prop="template_name">
<el-input
v-model.trim="formData.template_name"
clearable
:placeholder="t('templateNamePlaceholder')"
class="input-width"
maxlength="30"
/>
<el-input v-model.trim="formData.template_name" clearable :placeholder="t('templateNamePlaceholder')" class="input-width" maxlength="30" />
</el-form-item>
<el-form-item :label="t('expressCompany')" prop="express_company_id">
<el-select
v-model="formData.express_company_id"
:placeholder="t('expressCompanyPlaceholder')"
clearable
@change="handleSelectCompanyChange"
>
<el-option
v-for="item in companyList"
:key="item.company_id"
:label="item.company_name"
:value="item.company_id"
/>
<el-select v-model="formData.express_company_id" :placeholder="t('expressCompanyPlaceholder')" clearable @change="handleSelectCompanyChange">
<el-option v-for="item in companyList" :key="item.company_id" :label="item.company_name" :value="item.company_id" />
</el-select>
</el-form-item>
<el-form-item
:label="t('expType')"
prop="exp_type"
v-show="expTypeList.length"
>
<el-form-item :label="t('expType')" prop="exp_type" v-show="expTypeList.length">
<el-radio-group v-model="formData.exp_type">
<el-radio
v-for="(item, index) in expTypeList"
:key="index"
:value="item.value"
>{{ item.text }}</el-radio
>
<el-radio v-for="(item,index) in expTypeList" :key="index" :value="item.value">{{ item.text }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('printStyle')" v-show="printStyleList.length">
<div>
<el-select
v-model="formData.print_style"
:placeholder="t('printStylePlaceholder')"
clearable
>
<el-option
v-for="(item, index) in printStyleList"
:key="index"
:label="item.template_name"
:value="item.template_size"
/>
<el-select v-model="formData.print_style" :placeholder="t('printStylePlaceholder')" clearable>
<el-option v-for="(item,index) in printStyleList" :key="index" :label="item.template_name" :value="item.template_size" />
</el-select>
<div class="text-[12px] text-[#999] mt-[3px] leading-[20px]">
{{ t('printStyleTips1') }}
</div>
<div class="text-[12px] text-[#999] mt-[3px] leading-[20px]">
{{ t('printStyleTips2') }}
</div>
<div class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('printStyleTips1') }}</div>
<div class="text-[12px] text-[#999] mt-[3px] leading-[20px]">{{ t('printStyleTips2') }}</div>
</div>
</el-form-item>
</el-card>
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-sm">{{ t('otherSettings') }}</h3>
<el-form-item :label="t('customerName')">
<div>
<el-input
v-model.trim="formData.customer_name"
clearable
class="input-width"
maxlength="20"
/>
<div
class="flex items-center mt-[5px] text-[12px] text-[#999] leading-[20px]"
>
<el-input v-model.trim="formData.customer_name" clearable class="input-width" maxlength="20" />
<div class="flex items-center mt-[5px] text-[12px] text-[#999] leading-[20px]">
<span>{{ t('customerNameTips') }}</span>
<a
class="ml-[3px] text-[var(--el-color-primary)]"
target="_blank"
href="https://www.yuque.com/kdnjishuzhichi/rg4owd"
>{{ t('examine') }}</a
>
<a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://www.yuque.com/kdnjishuzhichi/rg4owd">{{t('examine')}}</a>
</div>
<div
class="flex items-center mt-[3px] text-[12px] text-[#999] leading-[20px]"
>
<div class="flex items-center mt-[3px] text-[12px] text-[#999] leading-[20px]">
<span>{{ t('customerNameTips1') }}</span>
<a
class="ml-[3px] text-[var(--el-color-primary)]"
target="_blank"
href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/hrfw43"
>{{ t('examine') }}</a
>
<a class="ml-[3px] text-[var(--el-color-primary)]" target="_blank" href="https://www.yuque.com/kdnjishuzhichi/dfcrg1/hrfw43">{{t('examine')}}</a>
</div>
</div>
</el-form-item>
<el-form-item :label="t('customerPwd')">
<div>
<el-input
v-model.trim="formData.customer_pwd"
clearable
class="input-width"
maxlength="20"
/>
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">
{{ t('customerPwdTips') }}
</div>
<el-input v-model.trim="formData.customer_pwd" clearable class="input-width" maxlength="20" />
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('customerPwdTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('sendSite')">
<div>
<el-input
v-model.trim="formData.send_site"
clearable
class="input-width"
maxlength="20"
/>
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">
{{ t('sendSiteTips') }}
</div>
<el-input v-model.trim="formData.send_site" clearable class="input-width" maxlength="20" />
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('sendSiteTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('sendStaff')">
<div>
<el-input
v-model.trim="formData.send_staff"
clearable
class="input-width"
maxlength="20"
/>
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">
{{ t('sendStaffTips') }}
</div>
<el-input v-model.trim="formData.send_staff" clearable class="input-width" maxlength="20" />
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('sendStaffTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('monthCode')">
<div>
<el-input
v-model.trim="formData.month_code"
clearable
class="input-width"
maxlength="20"
/>
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">
{{ t('monthCodeTips') }}
</div>
<el-input v-model.trim="formData.month_code" clearable class="input-width" maxlength="20" />
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('monthCodeTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('payType')">
<el-radio-group v-model="formData.pay_type">
<el-radio
v-for="(item, index) in payType"
:value="parseInt(index)"
>{{ item }}</el-radio
>
<el-radio v-for="(item,index) in payType" :value="parseInt(index)">{{ item }}</el-radio>
</el-radio-group>
</el-form-item>
@ -186,35 +92,24 @@
<el-radio :value="1">{{ t('yes') }}</el-radio>
<el-radio :value="0">{{ t('no') }}</el-radio>
</el-radio-group>
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">
{{ t('isNoticeTips') }}
</div>
<div class="mt-[5px] text-[12px] text-[#999] leading-[20px]">{{ t('isNoticeTips') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('status')">
<el-switch
v-model="formData.status"
:active-value="1"
:inactive-value="0"
/>
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item :label="t('isDefault')">
<el-switch
v-model="formData.is_default"
:active-value="1"
:inactive-value="0"
/>
<el-switch v-model="formData.is_default" :active-value="1" :inactive-value="0" />
</el-form-item>
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="repeat" @click="confirm(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" :loading="repeat" @click="confirm(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
@ -230,7 +125,7 @@ import {
addElectronicSheet,
editElectronicSheet,
getElectronicSheetInfo,
getElectronicSheetPayType,
getElectronicSheetPayType
} from '@/addon/shop/api/electronic_sheet'
import { getCompanyList } from '@/addon/shop/api/delivery'
@ -269,19 +164,11 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
template_name: [
{
required: true,
message: t('templateNamePlaceholder'),
trigger: 'blur',
},
{ required: true, message: t('templateNamePlaceholder'), trigger: 'blur' },
],
express_company_id: [
{
required: true,
message: t('expressCompanyPlaceholder'),
trigger: 'blur',
},
],
{ required: true, message: t('expressCompanyPlaceholder'), trigger: 'blur' },
]
}
})
@ -292,19 +179,18 @@ const payType = ref([])
const init = async ()=> {
getElectronicSheetPayType().then((res: any) => {
payType.value = res.data
payType.value = res.data;
})
await getCompanyList({ electronic_sheet_switch: 1 }).then((res: any) => {
companyList.value = res.data
companyList.value = res.data;
})
if (formData.id) {
loading.value = true
getElectronicSheetInfo(formData.id).then((res: any) => {
let data = res.data
if (data)
Object.keys(formData).forEach((key: string) => {
let data = res.data;
if (data) Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
})
loading.value = false
@ -313,38 +199,39 @@ const init = async () => {
}
}
init()
init();
const handleSelectCompanyChange = (value: any,load: any = false) => {
if (!value) {
expTypeList.value = []
printStyleList.value = []
return
expTypeList.value = [];
printStyleList.value = [];
return;
}
for (let i = 0; i < companyList.value.length; i++) {
if (companyList.value[i].company_id == value) {
expTypeList.value = companyList.value[i].exp_type
expTypeList.value = companyList.value[i].exp_type;
expTypeList.value.forEach((item: any) => {
if (item.value) item.value = parseInt(item.value)
if (item.value) item.value = parseInt(item.value);
})
printStyleList.value = companyList.value[i].print_style
printStyleList.value = companyList.value[i].print_style;
if (!load) {
if (expTypeList.value.length) {
formData.exp_type = expTypeList.value[0].value
} else {
formData.exp_type = 1 // 1
formData.exp_type = 1; // 1
}
if (printStyleList.value.length) {
formData.print_style = printStyleList.value[0].value
} else {
formData.print_style = '' //
formData.print_style = ''; //
}
}
break
break;
}
}
}
/**
@ -357,19 +244,18 @@ const confirm = async (formEl: FormInstance | undefined) => {
await formEl.validate(async(valid) => {
if (valid) {
if (repeat.value) return
repeat.value = true
let data = formData
save(data)
.then((res) => {
save(data).then(res => {
repeat.value = false
if (!formData.id) {
router.push('/shop/delivery/electronic_sheet')
}
})
.catch((err) => {
}).catch(err => {
repeat.value = false
})
}

283
admin/src/addon/shop/views/delivery/local.vue

@ -5,14 +5,7 @@
</el-card>
<el-card class="box-card !border-none" shadow="never">
<el-form
label-width="120px"
ref="formRef"
:rules="formRules"
:model="formData"
class="page-form"
v-loading="loading"
>
<el-form label-width="120px" ref="formRef" :rules="formRules" :model="formData" class="page-form" v-loading="loading">
<!-- <h3 class="panel-title">{{t('basicSettings')}}</h3> -->
<el-form-item :label="t('deliveryType')" prop="delivery_type">
<el-checkbox-group v-model="formData.delivery_type">
@ -50,32 +43,11 @@
<el-form-item :label="t('deliveryAddress')" prop="delivery_address">
<div class="flex flex-col">
<div class="flex">
{{
defaultDeliveryAddress
? defaultDeliveryAddress.full_address
: t('defaultDeliveryAddressEmpty')
}}
<el-button
type="primary"
@click="router.push('/shop/order/address')"
link
class="ml-[10px]"
>{{
defaultDeliveryAddress ? t('update') : t('toSetting')
}}</el-button
>
</div>
<div
class="text-error leading-none"
v-if="
formData.center.lat &&
defaultDeliveryAddress &&
(formData.center.lat != defaultDeliveryAddress.lat ||
formData.center.lng != defaultDeliveryAddress.lng)
"
>
{{ t('deliveryAddressChange') }}
{{ defaultDeliveryAddress ? defaultDeliveryAddress.full_address : t('defaultDeliveryAddressEmpty') }}
<el-button type="primary" @click="router.push('/shop/order/address')" link class="ml-[10px]">{{ defaultDeliveryAddress ? t('update') : t('toSetting') }}</el-button>
</div>
<div class="text-error leading-none" v-if="formData.center.lat && defaultDeliveryAddress && (formData.center.lat != defaultDeliveryAddress.lat || formData.center.lng != defaultDeliveryAddress.lng)">
{{ t('deliveryAddressChange') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('feeType')">
@ -84,42 +56,22 @@
<el-radio label="distance">{{ t('distance') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('feeSetting')"
prop="distance"
v-show="formData.fee_type == 'distance'"
>
<el-form-item :label="t('feeSetting')" prop="distance" v-show="formData.fee_type == 'distance'">
<div class="flex">
<div class="w-[60px] mx-[5px]">
<el-input
v-model.number="formData.base_dist"
type="text"
@keyup="filterDigit($event)"
/>
<el-input v-model.number="formData.base_dist" type="text" @keyup="filterDigit($event)" />
</div>
{{ t('feeSettingTextOne') }}
<div class="w-[60px] mx-[5px]">
<el-input
v-model.trim="formData.base_price"
type="text"
@keyup="filterDigit($event)"
/>
<el-input v-model.trim="formData.base_price" type="text" @keyup="filterDigit($event)"/>
</div>
{{ t('feeSettingTextTwo') }}
<div class="w-[60px] mx-[5px]">
<el-input
v-model.number="formData.grad_dist"
type="text"
@keyup="filterDigit($event)"
/>
<el-input v-model.number="formData.grad_dist" type="text" @keyup="filterDigit($event)"/>
</div>
{{ t('feeSettingTextThree') }}
<div class="w-[60px] mx-[5px]">
<el-input
v-model.trim="formData.grad_price"
type="text"
@keyup="filterDigit($event)"
/>
<el-input v-model.trim="formData.grad_price" type="text" @keyup="filterDigit($event)"/>
</div>
{{ t('priceUnit') }}
</div>
@ -128,27 +80,15 @@
<div class="flex">
{{ t('weightFeeTextOne') }}
<div class="w-[60px] mx-[5px]">
<el-input
v-model.trim="formData.weight_start"
type="text"
@keyup="filterDigit($event)"
/>
<el-input v-model.trim="formData.weight_start" type="text" @keyup="filterDigit($event)"/>
</div>
{{ t('weightFeeTextTwo') }}
<div class="w-[60px] mx-[5px]">
<el-input
v-model.trim="formData.weight_unit"
type="text"
@keyup="filterDigit($event)"
/>
<el-input v-model.trim="formData.weight_unit" type="text" @keyup="filterDigit($event)"/>
</div>
{{ t('weightFeeTextThree') }}
<div class="w-[60px] mx-[5px]">
<el-input
v-model.trim="formData.weight_price"
type="text"
@keyup="filterDigit($event)"
/>
<el-input v-model.trim="formData.weight_price" type="text" @keyup="filterDigit($event)" />
</div>
{{ t('priceUnit') }}
</div>
@ -157,89 +97,36 @@
<el-form-item prop="area" v-loading="mapLoading">
<div class="relative w-full">
<div id="container" class="w-full h-[520px]"></div>
<div
class="absolute bg-white w-[270px] h-[500px] top-[10px] left-[10px] region-list"
>
<div class="absolute bg-white w-[270px] h-[500px] top-[10px] left-[10px] region-list">
<el-scrollbar>
<div
class="p-[10px] region-item pr-[50px] relative"
v-for="(item, index) in formData.area"
:key="index"
:class="{ '!border-primary': index == currArea }"
@click="selectArea(index)"
>
<el-form
label-width="80px"
:model="item"
:rules="formRules"
class="page-form"
ref="areaFromRef"
>
<div class="p-[10px] region-item pr-[50px] relative" v-for="(item, index) in formData.area" :key="index" :class="{ '!border-primary': index == currArea }" @click="selectArea(index)">
<el-form label-width="80px" :model="item" :rules="formRules" class="page-form" ref="areaFromRef">
<div class="pb-[18px]">
<el-form-item :label="t('areaName')" prop="area_name">
<el-input
v-model.trim="formData.area[index].area_name"
type="text"
/>
<el-input v-model.trim="formData.area[index].area_name" type="text" />
</el-form-item>
</div>
<div class="pb-[18px]">
<el-form-item :label="t('startPrice')" prop="start_price">
<el-input
v-model.trim="formData.area[index].start_price"
type="text"
@keyup="filterDigit($event)"
/>
<el-input v-model.trim="formData.area[index].start_price" type="text" @keyup="filterDigit($event)" />
</el-form-item>
</div>
<div
class="pb-[10px]"
v-show="formData.fee_type == 'region'"
>
<el-form-item
:label="t('deliveryPrice')"
prop="delivery_price"
>
<el-input
v-model.trim="formData.area[index].delivery_price"
type="text"
@keyup="filterDigit($event)"
/>
<div class="pb-[10px]" v-show="formData.fee_type == 'region'">
<el-form-item :label="t('deliveryPrice')" prop="delivery_price">
<el-input v-model.trim="formData.area[index].delivery_price" type="text" @keyup="filterDigit($event)"/>
</el-form-item>
</div>
<el-form-item :label="t('areaType')">
<el-radio-group
v-model="formData.area[index].area_type"
@click.stop=""
@change="areaTypeChange(index)"
>
<el-radio
label="radius"
size="large"
class="!mr-[10px]"
>{{ t('radius') }}</el-radio
>
<el-radio
label="custom"
size="large"
class="!mr-[0px]"
>{{ t('custom') }}</el-radio
>
<el-radio-group v-model="formData.area[index].area_type" @click.stop="" @change="areaTypeChange(index)">
<el-radio label="radius" size="large" class="!mr-[10px]">{{ t('radius') }}</el-radio>
<el-radio label="custom" size="large" class="!mr-[0px]">{{ t('custom') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-button
type="primary"
link
class="absolute z-1 top-[10px] right-[10px]"
@click.stop="deleteArea(index)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link class="absolute z-1 top-[10px] right-[10px]" @click.stop="deleteArea(index)">{{ t('delete') }}</el-button>
</div>
<div class="p-[10px] text-center">
<el-button type="default" plain @click="addArea">{{
t('addDeliveryArea')
}}</el-button>
<el-button type="default" plain @click="addArea">{{ t('addDeliveryArea') }}</el-button>
</div>
</el-scrollbar>
</div>
@ -249,12 +136,7 @@
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button
type="primary"
@click="onSave(formRef)"
:disabled="loading"
>{{ t('save') }}</el-button
>
<el-button type="primary" @click="onSave(formRef)" :disabled="loading">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
@ -267,13 +149,7 @@ import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import { getMap } from '@/app/api/sys'
import { guid, filterDigit, deepClone } from '@/utils/common'
import {
createCircle,
deleteGeometry,
createPolygon,
selectGeometry,
createMarker,
} from '@/utils/qqmap'
import { createCircle, deleteGeometry, createPolygon, selectGeometry, createMarker } from '@/utils/qqmap'
import { setLocal, getLocal } from '@/addon/shop/api/delivery'
import { FormInstance } from 'element-plus'
import Test from '@/utils/test'
@ -292,18 +168,16 @@ interface addressType {
}
const defaultDeliveryAddress:any = ref<addressType|null>(null)
const getDefaultDeliveryAddress = async () => {
await getShopDefaultDeliveryAddressInfo()
.then(({ data }) => {
await getShopDefaultDeliveryAddressInfo().then(({ data }) => {
defaultDeliveryAddress.value = data
})
.catch()
}).catch()
}
getDefaultDeliveryAddress()
const formData = ref({
center: {
lat: '',
lng: '',
lng: ''
},
delivery_type: ['business'],
fee_type: 'region',
@ -314,7 +188,7 @@ const formData = ref({
base_price: '',
grad_dist: '',
grad_price: '',
weight_start: 0.0,
weight_start: 0.000,
weight_unit: 0,
weight_price: 0,
area: [
@ -324,18 +198,16 @@ const formData = ref({
start_price: 0,
delivery_price: 0,
area_json: {
key: guid(),
},
},
],
key: guid()
}
}
]
})
//
const formRules = computed(() => {
return {
time_week: [
{ required: true, message: t('timeWeekRequire'), trigger: 'change' },
],
time_week: [{ required: true, message: t('timeWeekRequire'), trigger: 'change' }],
delivery_address: [
{
validator: (rule: any, value: any, callback: any) => {
@ -343,8 +215,8 @@ const formRules = computed(() => {
callback(new Error(t('defaultDeliveryAddressEmpty')))
}
callback()
},
},
}
}
],
delivery_type: [
{
@ -353,8 +225,8 @@ const formRules = computed(() => {
callback(new Error(t('deliveryTypeRequire')))
}
callback()
},
},
}
}
],
distance: [
{
@ -375,12 +247,10 @@ const formRules = computed(() => {
}
callback()
},
trigger: 'blur',
},
],
area_name: [
{ required: true, message: t('areaNameRequire'), trigger: 'blur' },
trigger: 'blur'
}
],
area_name: [{ required: true, message: t('areaNameRequire'), trigger: 'blur' }],
start_price: [
{ required: true, message: t('startPriceRequire'), trigger: 'blur' },
{
@ -390,15 +260,11 @@ const formRules = computed(() => {
}
callback()
},
trigger: 'blur',
},
trigger: 'blur'
}
],
delivery_price: [
{
required: formData.value.fee_type == 'region',
message: t('deliveryPriceRequire'),
trigger: 'blur',
},
{ required: formData.value.fee_type == 'region', message: t('deliveryPriceRequire'), trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (parseInt(value) < 0) {
@ -406,8 +272,8 @@ const formRules = computed(() => {
}
callback()
},
trigger: 'blur',
},
trigger: 'blur'
}
],
area: [
{
@ -417,31 +283,25 @@ const formRules = computed(() => {
}
callback()
},
trigger: 'blur',
},
],
trigger: 'blur'
}
]
}
})
getLocal()
.then(({ data }) => {
getLocal().then(({ data }) => {
loading.value = false
if (data) Object.assign(formData.value, data)
formData.value.time_week = formData.value.time_week
? formData.value.time_week.split(',')
: []
})
.catch(() => {
formData.value.time_week = formData.value.time_week?formData.value.time_week.split(','):[]
}).catch(()=>{
loading.value = false
})
onMounted(() => {
const mapScript = document.createElement('script')
getMap().then((res) => {
getMap().then(res => {
mapScript.type = 'text/javascript'
mapScript.src =
'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' +
res.data.key
mapScript.src = 'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' + res.data.key
document.body.appendChild(mapScript)
})
mapScript.onload = () => {
@ -459,14 +319,11 @@ const mapLoading = ref(true)
const initMap = () => {
const TMap = (window as any).TMap
const LatLng = TMap.LatLng
const center = new LatLng(
defaultDeliveryAddress.value ? defaultDeliveryAddress.value.lat : 39.980619,
defaultDeliveryAddress.value ? defaultDeliveryAddress.value.lng : 116.321277
)
const center = new LatLng(defaultDeliveryAddress.value ? defaultDeliveryAddress.value.lat : 39.980619, defaultDeliveryAddress.value ? defaultDeliveryAddress.value.lng : 116.321277)
map = new TMap.Map('container', {
center,
zoom: 14,
zoom: 14
})
createMarker(map)
@ -474,10 +331,8 @@ const initMap = () => {
mapLoading.value = false
})
formData.value.area.forEach((item) => {
item.area_type == 'radius'
? createCircle(map, item.area_json)
: createPolygon(map, item.area_json)
formData.value.area.forEach(item => {
item.area_type == 'radius' ? createCircle(map, item.area_json) : createPolygon(map, item.area_json)
})
}
@ -493,8 +348,8 @@ const addArea = () => {
start_price: 0,
delivery_price: 0,
area_json: {
key: guid(),
},
key: guid()
}
})
const index = formData.value.area.length - 1
createCircle(map, formData.value.area[index].area_json)
@ -518,9 +373,7 @@ const selectArea = (index: number) => {
const areaTypeChange = (index: number) => {
const data = formData.value.area[index]
deleteGeometry(data.area_json.key)
data.area_type == 'radius'
? createCircle(map, data.area_json)
: createPolygon(map, data.area_json)
data.area_type == 'radius' ? createCircle(map, data.area_json) : createPolygon(map, data.area_json)
}
onBeforeUnmount(() => {
@ -547,17 +400,15 @@ const onSave = async (formEl: FormInstance | undefined) => {
formData.value.center = {
lat: defaultDeliveryAddress.value.lat,
lng: defaultDeliveryAddress.value.lng,
lng: defaultDeliveryAddress.value.lng
}
await formEl.validate(async (valid) => {
const param = deepClone(toRaw(formData.value))
param.time_week = param.time_week.toString()
setLocal(param)
.then(() => {
setLocal(param).then(() => {
loading.value = false
})
.catch(() => {
}).catch(() => {
loading.value = false
})
})

80
admin/src/addon/shop/views/delivery/search.vue

@ -1,20 +1,9 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header
:content="pageName"
:icon="ArrowLeft"
@back="router.push({ path: '/shop/order/delivery' })"
/>
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push({ path: '/shop/order/delivery' })" />
</el-card>
<el-form
:model="formData"
label-width="150px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form :model="formData" label-width="150px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<el-form-item :label="t('interfaceType')" prop="interface_type">
<div>
@ -22,23 +11,10 @@
<el-radio :label="1" size="large">{{ t('kdn') }}</el-radio>
<!-- <el-radio :label="2" size="large">{{ t('kd100') }}</el-radio>-->
</el-radio-group>
<p
class="text-[12px] text-[#b2b2b2]"
v-if="formData.interface_type == 1"
>
{{ t('promptTips1-1')
}}<el-button
class="button-size"
type="primary"
link
@click="openEvent('https://www.kdniao.com')"
>https://www.kdniao.com</el-button
>
<p class="text-[12px] text-[#b2b2b2]" v-if="formData.interface_type == 1">
{{ t('promptTips1-1') }}<el-button class="button-size" type="primary" link @click="openEvent('https://www.kdniao.com')">https://www.kdniao.com</el-button>
</p>
<p
class="text-[12px] text-[#b2b2b2]"
v-if="formData.interface_type == 1"
>
<p class="text-[12px] text-[#b2b2b2]" v-if="formData.interface_type == 1">
{{ t('promptTips1-2') }}
</p>
<!-- <p class="text-[12px] text-[#b2b2b2]" v-if="formData.interface_type == 2">-->
@ -47,11 +23,7 @@
</div>
</el-form-item>
<div v-if="formData.interface_type == 1">
<el-form-item
:label="t('isPayEdition')"
prop="kdn_is_pay"
class="items-center"
>
<el-form-item :label="t('isPayEdition')" prop="kdn_is_pay" class="items-center">
<el-radio-group v-model="formData.kdniao_is_pay">
<el-radio :label="1" size="large">{{ t('free') }}</el-radio>
<el-radio :label="2" size="large">{{ t('pay') }}</el-radio>
@ -60,29 +32,18 @@
<el-form-item label="EBusinessID" class="input-item">
<div>
<el-input
v-model.trim="formData.kdniao_id"
:placeholder="t('kdnEBusinessIDPlaceholder')"
class="input-width"
clearable
/>
<p class="text-[12px] text-[#b2b2b2]">
{{ t('kdnEBusinessIDTips') }}
</p>
<el-input v-model.trim="formData.kdniao_id" :placeholder="t('kdnEBusinessIDPlaceholder')" class="input-width" clearable />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kdnEBusinessIDTips') }}</p>
</div>
</el-form-item>
<el-form-item label="APPKEY" class="input-item">
<div>
<el-input
v-model.trim="formData.kdniao_app_key"
clearable
:placeholder="t('kdnAppKeyPlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.kdniao_app_key" clearable :placeholder="t('kdnAppKeyPlaceholder')" class="input-width" />
<p class="text-[12px] text-[#b2b2b2]">{{ t('kdnAppKeyTips') }}</p>
</div>
</el-form-item>
</div>
<!-- <div v-if="formData.interface_type == 2">-->
@ -100,14 +61,13 @@
<!-- </div>-->
<!-- </el-form-item>-->
<!-- </div>-->
</el-card>
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" :loading="loading" @click="save(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" :loading="loading" @click="save(formRef)">{{ t('save') }}</el-button>
</div>
</div>
</div>
@ -140,7 +100,7 @@ const formData = reactive<formDataType | any>({
kdniao_app_key: '',
kdniao_is_pay: 1,
kd100_app_key: '',
kd100_customer: '',
kd100_customer: ''
})
const setFormData = async () => {
@ -160,7 +120,8 @@ const openEvent = (url: any) => {
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({})
const formRules = reactive<FormRules>({
})
/**
* 保存
@ -171,21 +132,20 @@ const save = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
setDeliverySearch(formData)
.then(() => {
setDeliverySearch(formData).then(() => {
loading.value = false
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
})
}
</script>
<style lang="scss" scoped>
.input-item {
margin-bottom: 10px !important;
margin-bottom: 10px !important
}
.button-size {
@ -193,6 +153,6 @@ const save = async (formEl: FormInstance | undefined) => {
}
.el-radio.el-radio--large {
height: auto !important;
height: auto !important
}
</style>

96
admin/src/addon/shop/views/delivery/staff.vue

@ -14,84 +14,43 @@
{{ t('addDeliveryPersonnel') }}
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="tableData.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('deliverName')" prop="deliver_name">
<el-input
v-model.trim="tableData.searchParam.deliver_name"
:placeholder="t('deliverNamePlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.deliver_name" :placeholder="t('deliverNamePlaceholder')" />
</el-form-item>
<el-form-item :label="t('deliverMobile')" prop="deliver_mobile">
<el-input
v-model.trim="tableData.searchParam.deliver_mobile"
:placeholder="t('deliverMobilePlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.deliver_mobile" :placeholder="t('deliverMobilePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="getShopDeliveryFn()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="getShopDeliveryFn()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="tableData.data"
ref="tableRef"
size="large"
v-loading="tableData.loading"
>
<el-table :data="tableData.data" ref="tableRef" size="large" v-loading="tableData.loading">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column prop="deliver_name" :label="t('deliverName')" />
<el-table-column prop="deliver_mobile" :label="t('deliverMobile')" />
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.deliver_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.deliver_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="getShopDeliveryFn()"
@current-change="getShopDeliveryFn"
/>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
@size-change="getShopDeliveryFn()" @current-change="getShopDeliveryFn" />
</div>
</div>
</el-card>
<delivery-personnel-edit
ref="editCategoryDialog"
@complete="getShopDeliveryFn"
/>
<delivery-personnel-edit ref="editCategoryDialog" @complete="getShopDeliveryFn" />
</div>
</template>
<script lang="ts" setup>
@ -113,8 +72,8 @@ const tableData = reactive({
data: [],
searchParam: {
deliver_name: '',
deliver_mobile: '',
},
deliver_mobile: ''
}
})
const searchFormRef = ref<FormInstance>()
/**
@ -127,14 +86,12 @@ const getShopDeliveryFn = (page: number = 1) => {
getShopDelivery({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
})
.then((res) => {
...tableData.searchParam
}).then(res => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
})
.catch(() => {
}).catch(() => {
tableData.loading = false
})
}
@ -161,16 +118,17 @@ const editEvent = (data: any) => {
* 删除配送员
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('deliverDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('deliverDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteShopDeliver(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteShopDeliver(id).then(() => {
getShopDeliveryFn()
}).catch(() => {
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {

128
admin/src/addon/shop/views/delivery/store.vue

@ -15,55 +15,30 @@
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="storeTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="storeTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('storeName')" prop="store_name">
<el-input
v-model.trim="storeTable.searchParam.store_name"
:placeholder="t('storeNamePlaceholder')"
/>
<el-input v-model.trim="storeTable.searchParam.store_name" :placeholder="t('storeNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadStoreList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadStoreList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="storeTable.data"
size="large"
v-loading="storeTable.loading"
>
<el-table :data="storeTable.data" size="large" v-loading="storeTable.loading">
<template #empty>
<span>{{ !storeTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column :label="t('storeInfo')" min-width="170" align="left">
<template #default="{ row }">
<div class="h-[50px] flex items-center">
<el-image
class="w-[50px] h-[50px]"
:src="img(row.store_logo)"
fit="contain"
>
<el-image class="w-[50px] h-[50px] " :src="img(row.store_logo)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[50px] h-[50px]"
src="@/addon/shop/assets/store_default.png"
/>
<img class="w-[50px] h-[50px]" src="@/addon/shop/assets/store_default.png" />
</div>
</template>
</el-image>
@ -71,57 +46,30 @@
</div>
</template>
</el-table-column>
<el-table-column
prop="store_mobile"
:label="t('storeMobile')"
min-width="120"
/>
<el-table-column
prop="full_address"
:label="t('fullAddress')"
min-width="180"
/>
<el-table-column
prop="trade_time"
:label="t('tradeTime')"
min-width="120"
/>
<el-table-column prop="store_mobile" :label="t('storeMobile')" min-width="120" />
<el-table-column prop="full_address" :label="t('fullAddress')" min-width="180" />
<el-table-column prop="trade_time" :label="t('tradeTime')" min-width="120" />
<el-table-column :label="t('createTime')" min-width="120">
<template #default="{ row }">
{{ row.create_time || '' }}
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.store_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.store_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="storeTable.page"
v-model:page-size="storeTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="storeTable.total"
@size-change="loadStoreList()"
@current-change="loadStoreList"
/>
<el-pagination v-model:current-page="storeTable.page" v-model:page-size="storeTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="storeTable.total"
@size-change="loadStoreList()" @current-change="loadStoreList" />
</div>
</div>
</el-card>
</div>
</template>
@ -133,7 +81,7 @@ import { getStoreList, deleteStore } from '@/addon/shop/api/delivery'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const route = useRoute()
const pageName = route.meta.title
@ -146,8 +94,8 @@ const storeTable = reactive({
data: [],
searchParam: {
store_name: '',
create_time: '',
},
create_time: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -162,23 +110,17 @@ const loadStoreList = (page: number = 1) => {
getStoreList({
page: storeTable.page,
limit: storeTable.limit,
...storeTable.searchParam,
})
.then((res) => {
...storeTable.searchParam
}).then(res => {
storeTable.loading = false
storeTable.data = res.data.data
storeTable.total = res.data.total
setTablePageStorage(
storeTable.page,
storeTable.limit,
storeTable.searchParam
)
})
.catch(() => {
setTablePageStorage(storeTable.page, storeTable.limit, storeTable.searchParam);
}).catch(() => {
storeTable.loading = false
})
}
loadStoreList(getTablePageStorage(storeTable.searchParam).page)
loadStoreList(getTablePageStorage(storeTable.searchParam).page);
const router = useRouter()
@ -201,16 +143,17 @@ const editEvent = (data: any) => {
* 删除自提门店
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('storeDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('storeDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteStore(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteStore(id).then(() => {
loadStoreList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -221,4 +164,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

244
admin/src/addon/shop/views/delivery/store_edit.vue

@ -1,132 +1,59 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header
:content="id ? t('updateStore') : t('addStore')"
:icon="ArrowLeft"
@back="back"
/>
<el-page-header :content="id ? t('updateStore') : t('addStore')" :icon="ArrowLeft" @back="back" />
</el-card>
<el-card class="box-card !border-none" shadow="never" v-loading="loading">
<el-form
:model="formData"
label-width="140px"
ref="formRef"
:rules="formRules"
class="page-form"
>
<el-form :model="formData" label-width="140px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('storeName')" prop="store_name">
<el-input
v-model.trim="formData.store_name"
clearable
:placeholder="t('storeNamePlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.store_name" clearable :placeholder="t('storeNamePlaceholder')"
class="input-width" />
</el-form-item>
<el-form-item :label="t('storeDesc')">
<el-input
v-model.trim="formData.store_desc"
type="textarea"
rows="4"
clearable
:placeholder="t('storeDescPlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.store_desc" type="textarea" rows="4" clearable
:placeholder="t('storeDescPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('storeLogo')">
<upload-image v-model="formData.store_logo" />
</el-form-item>
<el-form-item :label="t('storeMobile')" prop="store_mobile">
<el-input
v-model.trim="formData.store_mobile"
clearable
:placeholder="t('storeMobilePlaceholder')"
class="input-width"
@keyup="filterNumber($event)"
@blur="formData.store_mobile = $event.target.value"
/>
<el-input v-model.trim="formData.store_mobile" clearable :placeholder="t('storeMobilePlaceholder')"
class="input-width" @keyup="filterNumber($event)"
@blur="formData.store_mobile = $event.target.value" />
</el-form-item>
<el-form-item :label="t('tradeTime')" prop="trade_time">
<div>
<el-input
v-model.trim="formData.trade_time"
clearable
:placeholder="t('tradeTimePlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.trade_time" clearable :placeholder="t('tradeTimePlaceholder')"
class="input-width" />
<p class="text-[12px] text-[#999]">{{ t('tradeTimeTips') }}</p>
</div>
</el-form-item>
<el-form-item :label="t('storeAddress')" prop="address_area">
<el-select
v-model="formData.province_id"
value-key="id"
clearable
class="w-[200px]"
ref="provinceRef"
>
<el-select v-model="formData.province_id" value-key="id" clearable class="w-[200px]" ref="provinceRef">
<el-option :label="t('provincePlaceholder')" :value="0"/>
<el-option
v-for="(item, index) in areaList.province"
:key="index"
:label="item.name"
:value="item.id"
/>
<el-option v-for="(item, index) in areaList.province" :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select
v-model="formData.city_id"
value-key="id"
clearable
class="w-[200px] ml-3"
ref="cityRef"
>
<el-select v-model="formData.city_id" value-key="id" clearable class="w-[200px] ml-3" ref="cityRef">
<el-option :label="t('cityPlaceholder')" :value="0"/>
<el-option
v-for="(item, index) in areaList.city"
:key="index"
:label="item.name"
:value="item.id"
/>
<el-option v-for="(item, index) in areaList.city " :key="index" :label="item.name" :value="item.id"/>
</el-select>
<el-select
v-model="formData.district_id"
value-key="id"
clearable
class="w-[200px] ml-3"
ref="districtRef"
>
<el-select v-model="formData.district_id" value-key="id" clearable class="w-[200px] ml-3" ref="districtRef">
<el-option :label="t('districtPlaceholder')" :value="0"/>
<el-option
v-for="(item, index) in areaList.district"
:key="index"
:label="item.name"
:value="item.id"
/>
<el-option v-for="(item, index) in areaList.district " :key="index" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item prop="address">
<el-input
v-model.trim="formData.address"
clearable
:placeholder="t('addressPlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.address" clearable :placeholder="t('addressPlaceholder')" class="input-width"/>
</el-form-item>
<el-form-item>
<div
id="container"
class="w-[800px] h-[520px] relative"
v-loading="mapLoading"
></div>
<div id="container" class="w-[800px] h-[520px] relative" v-loading="mapLoading"></div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer !z-[9999]">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
@ -148,14 +75,14 @@ const id: number = parseInt(route.query.id as string)
const loading = ref(false)
const pageName = route.meta.title
interface areaType{
province: any[]
city: any[]
province: any[],
city: any[],
district: any[]
}
const areaList = reactive<areaType>({
province: [],
city: [],
district: [],
district: []
})
const provinceRef = ref()
const cityRef = ref()
@ -164,19 +91,17 @@ const districtRef = ref()
/**
* 获取省
*/
getAreaListByPid(0).then((res) => {
getAreaListByPid(0).then(res => {
areaList.province = res.data
})
let mapKey: string = ''
onMounted(() => {
const mapScript = document.createElement('script')
getMap().then((res) => {
getMap().then(res => {
mapKey = res.data.key
mapScript.type = 'text/javascript'
mapScript.src =
'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' +
res.data.key
mapScript.src = 'https://map.qq.com/api/gljs?libraries=tools,service&v=1.exp&key=' + res.data.key
document.body.appendChild(mapScript)
})
mapScript.onload = () => {
@ -199,7 +124,7 @@ const initMap = () => {
map = new TMap.Map('container', {
center,
zoom: 14,
zoom: 14
})
map.on('tilesloaded', () => {
@ -212,7 +137,7 @@ const initMap = () => {
map.setCenter(evt.latLng)
marker.updateGeometries({
id: 'center',
position: evt.latLng,
position: evt.latLng
})
latLngChange(evt.latLng.lat, evt.latLng.lng)
})
@ -223,12 +148,11 @@ const initMap = () => {
const storeArea = reactive({
province_id: 0,
city_id: 0,
district_id: 0,
district_id: 0
})
const latLngChange = (lat: number, lng: number) => {
latLngToAddress({ mapKey, lat, lng })
.then(({ message, result }) => {
latLngToAddress({ mapKey, lat, lng }).then(({ message, result }) => {
if (message == 'query ok' || message == 'Success') {
formData.latitude = result.location.lat
formData.longitude = result.location.lng
@ -242,8 +166,7 @@ const latLngChange = (lat: number, lng: number) => {
} else {
console.error(message, result)
}
})
.catch((err) => {
}).catch(err => {
console.log(err)
})
}
@ -265,9 +188,9 @@ const initialFormData = {
district_name: '',
address: '',
full_address: '',
longitude: 116.39719,
longitude: 116.397190,
latitude: 39.908626,
trade_time: '',
trade_time: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -289,16 +212,16 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
store_name: [
{ required: true, message: t('storeNamePlaceholder'), trigger: 'blur' },
{ required: true, message: t('storeNamePlaceholder'), trigger: 'blur' }
],
store_logo: [
{ required: true, message: t('storeLogoPlaceholder'), trigger: 'blur' },
{ required: true, message: t('storeLogoPlaceholder'), trigger: 'blur' }
],
store_mobile: [
{ required: true, message: t('storeMobilePlaceholder'), trigger: 'blur' },
{ required: true, message: t('storeMobilePlaceholder'), trigger: 'blur' }
],
trade_time: [
{ required: true, message: t('tradeTimePlaceholder'), trigger: 'blur' },
{ required: true, message: t('tradeTimePlaceholder'), trigger: 'blur' }
],
address_area: [
{
@ -313,23 +236,21 @@ const formRules = computed(() => {
callback(new Error(t('districtPlaceholder')))
}
callback()
},
},
}
}
],
address: [
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('addressPlaceholder'), trigger: 'blur' }
]
}
})
/**
* 获取市
*/
watch(
() => formData.province_id,
(nval) => {
watch(() => formData.province_id, (nval) => {
if (nval) {
getAreaListByPid(formData.province_id).then((res) => {
getAreaListByPid(formData.province_id).then(res => {
areaList.city = res.data
const cityId = formData.city_id
@ -352,17 +273,14 @@ watch(
} else {
formData.city_id = 0
}
}
)
})
/**
* 获取区
*/
watch(
() => formData.city_id,
(nval) => {
watch(() => formData.city_id, (nval) => {
if (nval) {
getAreaListByPid(formData.city_id).then((res) => {
getAreaListByPid(formData.city_id).then(res => {
areaList.district = res.data
const districtId = formData.district_id
@ -385,91 +303,73 @@ watch(
} else {
formData.district_id = 0
}
}
)
})
watch(
() => formData.district_id,
(nval) => {
watch(() => formData.district_id, (nval) => {
if (nval) {
areaChange()
}
}
)
})
const areaChange = debounce(() => {
setTimeout(() => {
const address = [
formData.province_id ? provinceRef.value.states.selectedLabel : '',
formData.city_id ? cityRef.value.states.selectedLabel : '',
formData.district_id ? districtRef.value.states.selectedLabel : '',
formData.district_id ? districtRef.value.states.selectedLabel : ''
]
addressToLatLng({ mapKey, address: address.join('') }).then(
({ message, result }) => {
addressToLatLng({ mapKey, address: address.join('') }).then(({ message, result }) => {
if (message == 'Success' || message == 'query ok') {
const latLng = new (window as any).TMap.LatLng(
result.location.lat,
result.location.lng
)
const latLng = new (window as any).TMap.LatLng(result.location.lat, result.location.lng)
map.setCenter(latLng)
marker.updateGeometries({
id: 'center',
position: latLng,
position: latLng
})
formData.latitude = result.location.lat
formData.longitude = result.location.lng
} else {
console.error(message, result)
}
}
)
})
}, 500)
}, 500)
/**
* 地图点选获取市
*/
watch(
() => storeArea.province_id,
(nval) => {
watch(() => storeArea.province_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.province_id).then((res) => {
getAreaListByPid(storeArea.province_id).then(res => {
areaList.city = res.data
formData.province_id = storeArea.province_id
formData.city_id = storeArea.city_id
})
}
}
)
})
/**
* 地图点选获取区
*/
watch(
() => storeArea.city_id,
(nval) => {
watch(() => storeArea.city_id, (nval) => {
if (nval) {
getAreaListByPid(storeArea.city_id).then((res) => {
getAreaListByPid(storeArea.city_id).then(res => {
areaList.district = res.data
formData.city_id = storeArea.city_id
formData.district_id = storeArea.district_id
})
}
}
)
})
/**
* 地图点选获取区
*/
watch(
() => storeArea.district_id,
(nval) => {
watch(() => storeArea.district_id, (nval) => {
if (nval) {
formData.district_id = storeArea.district_id
}
}
)
})
const onSave = async (formEl: FormInstance | undefined) => {
if (loading.value || !formEl) return
@ -478,30 +378,22 @@ const onSave = async (formEl: FormInstance | undefined) => {
loading.value = true
const data = formData
;(formData.province_name = formData.province_id
? provinceRef.value.states.selectedLabel
: ''),
(formData.city_name = formData.city_id
? cityRef.value.states.selectedLabel
: ''),
(formData.district_name = formData.district_id
? districtRef.value.states.selectedLabel
: '')
formData.province_name = formData.province_id ? provinceRef.value.states.selectedLabel : '',
formData.city_name = formData.city_id ? cityRef.value.states.selectedLabel : '',
formData.district_name = formData.district_id ? districtRef.value.states.selectedLabel : ''
const address = [
data.province_id ? provinceRef.value.states.selectedLabel : '',
data.city_id ? cityRef.value.states.selectedLabel : '',
data.district_id ? districtRef.value.states.selectedLabel : '',
data.address,
data.address
]
data.full_address = address.join('')
const save = id ? editStore : addStore
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
history.back()
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}

132
admin/src/addon/shop/views/delivery/template.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<div class="detail-head !m-0">
<div class="left" @click="router.push('/shop/order/delivery')">
@ -15,93 +16,42 @@
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="templateTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="templateTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('templateName')" prop="template_name">
<el-input
v-model.trim="templateTable.searchParam.template_name"
:placeholder="t('templateNamePlaceholder')"
/>
<el-input v-model.trim="templateTable.searchParam.template_name" :placeholder="t('templateNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadTemplateList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadTemplateList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="templateTable.data"
size="large"
v-loading="templateTable.loading"
>
<el-table :data="templateTable.data" size="large" v-loading="templateTable.loading">
<template #empty>
<span>{{ !templateTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="template_name"
:label="t('templateName')"
min-width="120"
/>
<el-table-column
prop="fee_type_name"
:label="t('feeTypeName')"
min-width="120"
/>
<el-table-column
:label="t('freeShipping')"
min-width="120"
align="center"
>
<el-table-column prop="template_name" :label="t('templateName')" min-width="120" />
<el-table-column prop="fee_type_name" :label="t('feeTypeName')" min-width="120" />
<el-table-column :label="t('freeShipping')" min-width="120" align="center">
<template #default="{ row }">
{{ row.is_free_shipping ? t('open') : t('close') }}
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="120"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="120"
align="right"
>
<el-table-column prop="create_time" :label="t('createTime')" min-width="120" />
<el-table-column :label="t('operation')" fixed="right" min-width="120" align="right">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.template_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.template_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="templateTable.page"
v-model:page-size="templateTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="templateTable.total"
@size-change="loadTemplateList()"
@current-change="loadTemplateList"
/>
<el-pagination v-model:current-page="templateTable.page" v-model:page-size="templateTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="templateTable.total"
@size-change="loadTemplateList()" @current-change="loadTemplateList" />
</div>
</div>
</el-card>
@ -111,13 +61,10 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getShippingTemplatePageList,
deleteShippingTemplate,
} from '@/addon/shop/api/delivery'
import { getShippingTemplatePageList, deleteShippingTemplate } from '@/addon/shop/api/delivery'
import { ElMessageBox, FormInstance } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const route = useRoute()
const router = useRouter()
@ -130,8 +77,8 @@ const templateTable = reactive({
loading: true,
data: [],
searchParam: {
template_name: '',
},
template_name: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -146,23 +93,17 @@ const loadTemplateList = (page: number = 1) => {
getShippingTemplatePageList({
page: templateTable.page,
limit: templateTable.limit,
...templateTable.searchParam,
})
.then((res) => {
...templateTable.searchParam
}).then(res => {
templateTable.loading = false
templateTable.data = res.data.data
templateTable.total = res.data.total
setTablePageStorage(
templateTable.page,
templateTable.limit,
templateTable.searchParam
)
})
.catch(() => {
setTablePageStorage(templateTable.page, templateTable.limit, templateTable.searchParam);
}).catch(() => {
templateTable.loading = false
})
}
loadTemplateList(getTablePageStorage(templateTable.searchParam).page)
loadTemplateList(getTablePageStorage(templateTable.searchParam).page);
/**
* 添加运费模板
@ -176,26 +117,24 @@ const addEvent = () => {
* @param data
*/
const editEvent = (data: any) => {
router.push({
path: '/shop/order/shipping/template_edit',
query: { id: data.template_id },
})
router.push({ path: '/shop/order/shipping/template_edit', query: { id: data.template_id } })
}
/**
* 删除运费模板
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('templateDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('templateDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteShippingTemplate(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteShippingTemplate(id).then(() => {
loadTemplateList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -206,4 +145,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

311
admin/src/addon/shop/views/delivery/template_edit.vue

@ -5,22 +5,9 @@
</el-card>
<el-card class="box-card !border-none" shadow="never">
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('templateName')" prop="template_name">
<el-input
v-model.trim="formData.template_name"
clearable
:placeholder="t('templateNamePlaceholder')"
class="input-width"
maxlength="60"
/>
<el-input v-model.trim="formData.template_name" clearable :placeholder="t('templateNamePlaceholder')" class="input-width" maxlength="60" />
</el-form-item>
<el-form-item :label="t('feeTypeName')" prop="fee_type">
<el-radio-group v-model="formData.fee_type">
@ -34,239 +21,119 @@
<el-table-column :label="t('deliveryArea')">
<template #default="{ row, $index }">
<div class="area-input">
<span
v-if="$index"
@click="selectArea('fee', $index)"
class="cursor-pointer"
>{{
row.fee_area_names
? row.fee_area_names
: t('areaPlaceholder')
}}</span
>
<span v-else>{{
row.fee_area_names
? row.fee_area_names
: t('areaPlaceholder')
}}</span>
<span v-if="$index" @click="selectArea('fee', $index)" class="cursor-pointer">{{ row.fee_area_names ? row.fee_area_names : t('areaPlaceholder') }}</span>
<span v-else>{{ row.fee_area_names ? row.fee_area_names : t('areaPlaceholder') }}</span>
</div>
</template>
</el-table-column>
<el-table-column :label="feeLabel.first">
<template #default="{ $index }">
<el-input
v-model.trim="feeData[$index].snum"
maxlength="8"
@keyup="filterDigit($event)"
@blur="feeData[$index].snum = $event.target.value"
/>
<el-input v-model.trim="feeData[$index].snum" maxlength="8" @keyup="filterDigit($event)" @blur="feeData[$index].snum = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('fee')">
<template #default="{ $index }">
<el-input
v-model.trim="feeData[$index].sprice"
maxlength="8"
@keyup="filterDigit($event)"
@blur="feeData[$index].sprice = $event.target.value"
/>
<el-input v-model.trim="feeData[$index].sprice" maxlength="8" @keyup="filterDigit($event)" @blur="feeData[$index].sprice = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="feeLabel.continue">
<template #default="{ $index }">
<el-input
v-model.trim="feeData[$index].xnum"
maxlength="8"
@keyup="filterDigit($event)"
@blur="feeData[$index].xnum = $event.target.value"
/>
<el-input v-model.trim="feeData[$index].xnum" maxlength="8" @keyup="filterDigit($event)" @blur="feeData[$index].xnum = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('continueFee')">
<template #default="{ $index }">
<el-input
v-model.trim="feeData[$index].xprice"
@keyup="filterDigit($event)"
maxlength="8"
@blur="feeData[$index].xprice = $event.target.value"
/>
<el-input v-model.trim="feeData[$index].xprice" @keyup="filterDigit($event)" maxlength="8" @blur="feeData[$index].xprice = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" width="150">
<template #default="{ $index }">
<el-button
type="primary"
@click="delArea('fee', $index)"
link
v-if="$index"
>{{ t('delete') }}</el-button
>
<el-button type="primary" @click="delArea('fee', $index)" link v-if="$index">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[10px]">
<el-button type="primary" @click="addArea('fee')">{{
t('addDeliveryArea')
}}</el-button>
<el-button type="primary" @click="addArea('fee')">{{ t('addDeliveryArea') }}</el-button>
</div>
</el-form-item>
<!-- 指定区域包邮 -->
<el-form-item :label="t('freeShipping')" prop="is_free_shipping">
<el-switch
v-model="formData.is_free_shipping"
size="small"
:inactive-value="0"
:active-value="1"
/>
<el-switch v-model="formData.is_free_shipping" size="small" :inactive-value="0" :active-value="1" />
</el-form-item>
<el-form-item
v-show="formData.is_free_shipping"
prop="free_shipping_data"
>
<el-form-item v-show="formData.is_free_shipping" prop="free_shipping_data">
<el-table :data="freeShippingData" style="width: 100%" size="default">
<el-table-column :label="t('freeShippingArea')">
<template #default="{ row, $index }">
<div class="area-input">
<el-input
v-model.trim="row.free_shipping_area_names"
:placeholder="t('areaPlaceholder')"
readonly
@click="selectArea('free_shipping', $index)"
/>
<el-input v-model.trim="row.free_shipping_area_names" :placeholder="t('areaPlaceholder')" readonly @click="selectArea('free_shipping', $index)" />
</div>
</template>
</el-table-column>
<el-table-column :label="freeShippingLabel">
<template #default="{ $index }">
<el-input
v-model.trim="freeShippingData[$index].free_shipping_num"
@keyup="filterDigit($event)"
maxlength="8"
@blur="
freeShippingData[$index].free_shipping_num =
$event.target.value
"
/>
<el-input v-model.trim="freeShippingData[$index].free_shipping_num" @keyup="filterDigit($event)" maxlength="8" @blur="freeShippingData[$index].free_shipping_num = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('freeShippingPrice')">
<template #default="{ $index }">
<el-input
v-model.trim="freeShippingData[$index].free_shipping_price"
@keyup="filterDigit($event)"
maxlength="8"
@blur="
freeShippingData[$index].free_shipping_price =
$event.target.value
"
/>
<el-input v-model.trim="freeShippingData[$index].free_shipping_price" @keyup="filterDigit($event)" maxlength="8" @blur="freeShippingData[$index].free_shipping_price = $event.target.value"/>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" width="150">
<template #default="{ $index }">
<el-button
type="primary"
@click="delArea('free_shipping', $index)"
link
>{{ t('delete') }}</el-button
>
<el-button type="primary" @click="delArea('free_shipping', $index)" link>{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="form-tip">{{ t('freeShippingAreaTips') }}</div>
<div class="mt-[10px]">
<el-button type="primary" @click="addArea('free_shipping')">{{
t('addFreeShippingArea')
}}</el-button>
<el-button type="primary" @click="addArea('free_shipping')">{{ t('addFreeShippingArea') }}</el-button>
</div>
</el-form-item>
<!-- 指定区域不配送 -->
<el-form-item :label="t('noDelivery')" prop="no_delivery">
<el-switch
v-model="formData.no_delivery"
size="small"
:inactive-value="0"
:active-value="1"
/>
<el-switch v-model="formData.no_delivery" size="small" :inactive-value="0" :active-value="1" />
</el-form-item>
<el-form-item v-show="formData.no_delivery" prop="no_delivery_data">
<el-table :data="noDeliveryData" style="width: 100%" size="default">
<el-table-column :label="t('noDelivery')">
<template #default="{ row, $index }">
<div class="area-input">
<el-input
v-model.trim="row.no_delivery_area_names"
readonly
@click="selectArea('no_delivery', $index)"
:placeholder="t('areaPlaceholder')"
/>
<el-input v-model.trim="row.no_delivery_area_names" readonly @click="selectArea('no_delivery', $index)" :placeholder="t('areaPlaceholder')" />
</div>
</template>
</el-table-column>
<el-table-column :label="t('operation')" align="right" width="150">
<template #default="{ $index }">
<el-button
type="primary"
@click="delArea('no_delivery', $index)"
link
>{{ t('delete') }}</el-button
>
<el-button type="primary" @click="delArea('no_delivery', $index)" link>{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[10px]">
<el-button type="primary" @click="addArea('no_delivery')">{{
t('addNoDelivery')
}}</el-button>
<el-button type="primary" @click="addArea('no_delivery')">{{ t('addNoDelivery') }}</el-button>
</div>
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button
type="primary"
@click="onSave(formRef)"
:disabled="loading"
>{{ t('save') }}</el-button
>
<el-button type="primary" @click="onSave(formRef)" :disabled="loading">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
</div>
<!-- 选择地区弹窗 -->
<el-dialog
v-model="showSelectAreaDialog"
:title="t('selectArea')"
width="80%"
class="diy-dialog-wrap"
:destroy-on-close="true"
@opened="showSelectOpened"
>
<el-dialog v-model="showSelectAreaDialog" :title="t('selectArea')" width="80%" class="diy-dialog-wrap" :destroy-on-close="true" @opened="showSelectOpened">
<el-scrollbar height="50vh">
<el-tree
:data="areaTreeData"
:props="{ children: 'child', label: 'name' }"
default-expand-all
show-checkbox
ref="areaTreeRef"
:default-checked-keys="selectedArea"
node-key="id"
/>
<el-tree :data="areaTreeData" :props="{ children: 'child', label: 'name' }" default-expand-all show-checkbox ref="areaTreeRef" :default-checked-keys="selectedArea" node-key="id" />
</el-scrollbar>
<template #footer>
<span class="dialog-footer">
<el-button @click="showSelectAreaDialog = false">{{
t('cancel')
}}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirmSelectArea"
>{{ t('confirm') }}</el-button
>
<el-button @click="showSelectAreaDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loading" @click="confirmSelectArea">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -276,11 +143,7 @@
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import { ElTree, FormInstance, ElMessage } from 'element-plus'
import {
addShippingTemplate,
editShippingTemplate,
getShippingTemplateInfo,
} from '@/addon/shop/api/delivery'
import { addShippingTemplate, editShippingTemplate, getShippingTemplateInfo } from '@/addon/shop/api/delivery'
import { AnyObject } from '@/types/global'
import { useRoute, useRouter } from 'vue-router'
import { getAreatree } from '@/app/api/sys'
@ -303,7 +166,7 @@ const initialFormData = {
is_free_shipping: 0,
fee_data: [],
free_shipping_data: [],
no_delivery_data: [],
no_delivery_data: []
}
const pageName = route.meta.title
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -312,8 +175,7 @@ const areaTree = ref<AnyObject[]>([])
if (route.query.id) {
loading.value = true
getShippingTemplateInfo(route.query.id)
.then(({ data }) => {
getShippingTemplateInfo(route.query.id).then(({ data }) => {
if (data) {
Object.keys(formData).forEach((key: string) => {
if (data[key] != undefined) formData[key] = data[key]
@ -323,31 +185,24 @@ if (route.query.id) {
freeShippingData.value = data.free_shipping_data
}
loading.value = false
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
getAreatree(2)
.then((res) => {
getAreatree(2).then(res => {
areaTree.value = res.data
})
.catch()
}).catch()
//
const formRules = computed(() => {
return {
template_name: [
{
required: true,
message: t('templateNamePlaceholder'),
trigger: 'blur',
},
{ required: true, message: t('templateNamePlaceholder'), trigger: 'blur' }
],
fee_data: [{ validator: feeDataValidate }],
free_shipping_data: [{ validator: freeShippingDataValidate }],
no_delivery_data: [{ validator: noDeliveryDataValidate }],
no_delivery_data: [{ validator: noDeliveryDataValidate }]
}
})
@ -417,16 +272,16 @@ const feeLabel = computed(() => {
const label: AnyObject = {
num: {
first: t('firstNum'),
continue: t('continueNum'),
continue: t('continueNum')
},
weight: {
first: t('firstWeight'),
continue: t('continueWeight'),
continue: t('continueWeight')
},
volume: {
first: t('firstVolume'),
continue: t('continueVolume'),
},
continue: t('continueVolume')
}
}
return label[formData.fee_type]
})
@ -435,21 +290,14 @@ const freeShippingLabel = computed(() => {
const label: AnyObject = {
num: t('freeShippingNum'),
weight: t('freeShippingWeight'),
volume: t('freeShippingVolume'),
volume: t('freeShippingVolume')
}
return label[formData.fee_type]
})
//
const feeData = ref<AnyObject[]>([
{
area_ids: [0],
fee_area_names: '全国',
snum: 1,
sprice: 0,
xnum: 1,
xprice: 0,
},
{ area_ids: [0], fee_area_names: '全国', snum: 1, sprice: 0, xnum: 1, xprice: 0 }
])
//
const freeShippingData = ref<AnyObject[]>([])
@ -463,22 +311,10 @@ const noDeliveryData = ref<AnyObject[]>([])
const addArea = (type: string) => {
switch (type) {
case 'fee':
feeData.value.push({
area_ids: [],
fee_area_names: '',
snum: 1,
sprice: 0,
xnum: 1,
xprice: 0,
})
feeData.value.push({ area_ids: [], fee_area_names: '', snum: 1, sprice: 0, xnum: 1, xprice: 0 })
break
case 'free_shipping':
freeShippingData.value.push({
area_ids: [],
free_shipping_area_names: '',
free_shipping_num: 0,
free_shipping_price: 0,
})
freeShippingData.value.push({ area_ids: [], free_shipping_area_names: '', free_shipping_num: 0, free_shipping_price: 0 })
break
case 'no_delivery':
noDeliveryData.value.push({ area_ids: [], no_delivery_area_names: '' })
@ -536,7 +372,7 @@ const selectArea = (type: string, index: number) => {
}
const areaTreeData = computed(() => {
areaTree.value.forEach((province) => {
areaTree.value.forEach(province => {
province.child.forEach((city:any) => {
city.disabled = disabledArea.value.includes(city.id)
})
@ -550,7 +386,7 @@ const confirmSelectArea = () => {
const areaIds: number[] = []
const areaNames: string[] = []
nodes.forEach((item) => {
nodes.forEach(item => {
if (item.level == 2) {
areaIds.push(item.id)
areaNames.push(item.name)
@ -564,13 +400,11 @@ const confirmSelectArea = () => {
break
case 'free_shipping':
freeShippingData.value[currSelect.index].area_ids = areaIds
freeShippingData.value[currSelect.index].free_shipping_area_names =
areaNames.toString()
freeShippingData.value[currSelect.index].free_shipping_area_names = areaNames.toString()
break
case 'no_delivery':
noDeliveryData.value[currSelect.index].area_ids = areaIds
noDeliveryData.value[currSelect.index].no_delivery_area_names =
areaNames.toString()
noDeliveryData.value[currSelect.index].no_delivery_area_names = areaNames.toString()
break
}
showSelectAreaDialog.value = false
@ -600,71 +434,44 @@ const onSave = async (formEl: FormInstance | undefined) => {
}
loading.value = true
const data:AnyObject = {
template_id: formData.template_id,
template_name: formData.template_name,
fee_type: formData.fee_type,
no_delivery: formData.no_delivery,
is_free_shipping: formData.is_free_shipping,
is_free_shipping: formData.is_free_shipping
}
const area: AnyObject = {}
feeData.value.forEach((item) => {
feeData.value.forEach(item => {
item.area_ids.forEach((city: number) => {
area['city_' + city] = {
city_id: city,
fee_area_ids: item.area_ids.toString(),
fee_area_names: item.fee_area_names,
snum: item.snum,
sprice: item.sprice,
xnum: item.xnum,
xprice: item.xprice,
}
area['city_' + city] = { city_id: city, fee_area_ids: item.area_ids.toString(), fee_area_names: item.fee_area_names, snum: item.snum, sprice: item.sprice, xnum: item.xnum, xprice: item.xprice }
})
})
freeShippingData.value.forEach((item) => {
freeShippingData.value.forEach(item => {
item.area_ids.forEach((city: number) => {
if (area['city_' + city]) {
Object.assign(area['city_' + city], {
free_shipping_area_ids: item.area_ids.toString(),
free_shipping_area_names: item.free_shipping_area_names,
free_shipping_num: item.free_shipping_num,
free_shipping_price: item.free_shipping_price,
})
Object.assign(area['city_' + city], { free_shipping_area_ids: item.area_ids.toString(), free_shipping_area_names: item.free_shipping_area_names, free_shipping_num: item.free_shipping_num, free_shipping_price: item.free_shipping_price })
} else {
area['city_' + city] = {
city_id: city,
free_shipping_area_ids: item.area_ids.toString(),
free_shipping_area_names: item.free_shipping_area_names,
free_shipping_num: item.free_shipping_num,
free_shipping_price: item.free_shipping_price,
}
area['city_' + city] = { city_id: city, free_shipping_area_ids: item.area_ids.toString(), free_shipping_area_names: item.free_shipping_area_names, free_shipping_num: item.free_shipping_num, free_shipping_price: item.free_shipping_price }
}
})
})
noDeliveryData.value.forEach((item) => {
noDeliveryData.value.forEach(item => {
item.area_ids.forEach((city: number) => {
if (area['city_' + city]) {
Object.assign(area['city_' + city], {
no_delivery_area_ids: item.area_ids.toString(),
no_delivery_area_names: item.no_delivery_area_names,
})
Object.assign(area['city_' + city], { no_delivery_area_ids: item.area_ids.toString(), no_delivery_area_names: item.no_delivery_area_names })
} else {
area['city_' + city] = {
city_id: city,
no_delivery_area_ids: item.area_ids.toString(),
no_delivery_area_names: item.no_delivery_area_names,
}
area['city_' + city] = { city_id: city, no_delivery_area_ids: item.area_ids.toString(), no_delivery_area_names: item.no_delivery_area_names }
}
})
})
data.area = Object.values(area)
save(data)
.then(() => {
save(data).then(() => {
loading.value = false
router.push({ path: '/shop/order/shipping/template' })
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}

240
admin/src/addon/shop/views/diy/components/edit-goods-coupon.vue

@ -1,35 +1,22 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('selectStyle')" class="flex">
<span
class="text-primary flex-1 cursor-pointer"
@click="showCouponStyle"
>{{ diyStore.editComponent.styleName }}</span
>
<span class="text-primary flex-1 cursor-pointer" @click="showCouponStyle">{{ diyStore.editComponent.styleName }}</span>
<el-icon>
<ArrowRight />
</el-icon>
</el-form-item>
</el-form>
<el-dialog
v-model="showCouponDialog"
:title="t('selectStyle')"
width="500px"
>
<el-dialog v-model="showCouponDialog" :title="t('selectStyle')" width="500px">
<div class="flex flex-wrap">
<template v-for="(item,index) in couponStyleList" :key="index">
<div
:class="{
'border-primary': selectCouponStyle.value == item.value,
}"
@click="changeCouponStyle(item)"
class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] m-[6px] cursor-pointer border bg-gray-50"
>
<div :class="{ 'border-primary': selectCouponStyle.value == item.value }" @click="changeCouponStyle(item)" class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] m-[6px] cursor-pointer border bg-gray-50">
<img :src="img(item.url)" />
</div>
</template>
@ -37,160 +24,88 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="showCouponDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="confirmCouponStyle">{{
t('confirm')
}}</el-button>
<el-button @click="showCouponDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmCouponStyle">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('couponContent') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('couponTitle')">
<el-input
v-model.trim="diyStore.editComponent.couponTitle"
clearable
:maxlength="diyStore.editComponent.style == 'style-3' ? 4 : 8"
show-word-limit
/>
<el-input v-model.trim="diyStore.editComponent.couponTitle" clearable :maxlength="diyStore.editComponent.style == 'style-3' ? 4 : 8" show-word-limit/>
</el-form-item>
<el-form-item :label="t('couponSubTitle')">
<el-input
v-model.trim="diyStore.editComponent.couponSubTitle"
clearable
:maxlength="diyStore.editComponent.style == 'style-3' ? 7 : 10"
show-word-limit
/>
<el-input v-model.trim="diyStore.editComponent.couponSubTitle" clearable :maxlength="diyStore.editComponent.style == 'style-3' ? 7 : 10" show-word-limit/>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('couponData') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('selectCoupon')">
<el-radio-group
v-model="diyStore.editComponent.source"
:title="t('goodsSelectPopupSelectGoodsButton')"
>
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('allSources') }}</el-radio>
<el-radio label="custom">{{
t('manualSelectionSources')
}}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('manualSelectionSources')"
v-if="diyStore.editComponent.source == 'custom'"
>
<coupon-select-popup
ref="couponSelectPopupRef"
v-model="diyStore.editComponent.couponIds"
:min="1"
:max="20"
/>
<el-form-item :label="t('manualSelectionSources')" v-if="diyStore.editComponent.source == 'custom'">
<coupon-select-popup ref="couponSelectPopupRef" v-model="diyStore.editComponent.couponIds" :min="1" :max="20" />
</el-form-item>
<el-form-item
:label="t('couponNum')"
v-if="diyStore.editComponent.source == 'all'"
>
<el-slider
show-input
v-model="diyStore.editComponent.num"
:min="1"
max="20"
size="small"
class="goods-coupon-slider"
/>
<el-form-item :label="t('couponNum')" v-if="diyStore.editComponent.source == 'all'">
<el-slider show-input v-model="diyStore.editComponent.num" :min="1" max="20" size="small" class="goods-coupon-slider" />
</el-form-item>
<el-form-item
:label="t('couponBtnText')"
v-if="diyStore.editComponent.style != 'style-4'"
>
<el-input
v-model.trim="diyStore.editComponent.btnText"
clearable
maxlength="5"
show-word-limit
/>
<el-form-item :label="t('couponBtnText')" v-if="diyStore.editComponent.style != 'style-4'">
<el-input v-model.trim="diyStore.editComponent.btnText" clearable maxlength="5" show-word-limit/>
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div
class="edit-attr-item-wrap"
v-if="diyStore.editComponent.style == 'style-4'"
>
<h3 class="mb-[10px]">{{ t('couponTitleStyle') }}</h3>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.style == 'style-4'">
<h3 class="mb-[10px]">{{ t("couponTitleStyle") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('couponTitleColor')">
<el-color-picker
v-model="diyStore.editComponent.titleColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.titleColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('couponSubTitleColor')">
<el-color-picker
v-model="diyStore.editComponent.subTitleColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.subTitleColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
<div
class="edit-attr-item-wrap"
v-if="diyStore.editComponent.style == 'style-4'"
>
<h3 class="mb-[10px]">{{ t('couponItemStyle') }}</h3>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.style == 'style-4'">
<h3 class="mb-[10px]">{{ t("couponItemStyle") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('couponMoney')">
<el-color-picker
v-model="diyStore.editComponent.couponItem.moneyColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.couponItem.moneyColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('textColor')">
<el-color-picker
v-model="diyStore.editComponent.couponItem.textColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.couponItem.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('subTextColor')">
<el-color-picker
v-model="diyStore.editComponent.couponItem.subTextColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.couponItem.subTextColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('listFrameColor')">
<el-color-picker
v-model="diyStore.editComponent.couponItem.bgColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.couponItem.bgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsRounded')">
<el-slider
v-model="diyStore.editComponent.couponItem.aroundRadius"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.couponItem.aroundRadius" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
@ -198,6 +113,7 @@
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>
@ -221,26 +137,26 @@ diyStore.editComponent.verify = (index: number) => {
if(diyStore.value[index].couponIds.length == 0){
res.code = false
res.message = t('couponPlaceholder')
return res
return res;
}
}
if(diyStore.value[index].btnText == ''){
res.code = false
res.message = t('couponBtnTextPlaceholder')
return res
return res;
}
if(diyStore.value[index].couponTitle == ''){
res.code = false
res.message = t('couponTitlePlaceholder')
return res
return res;
}
if(diyStore.value[index].couponSubTitle == ''){
res.code = false
res.message = t('couponSubTitlePlaceholder')
return res
return res;
}
return res
@ -248,7 +164,7 @@ diyStore.editComponent.verify = (index: number) => {
const selectCouponStyle = reactive({
title: diyStore.editComponent.styleName,
value: diyStore.editComponent.style,
value: diyStore.editComponent.style
})
//
@ -256,84 +172,76 @@ const showCouponDialog = ref(false)
const showCouponStyle = () => {
showCouponDialog.value = true
selectCouponStyle.title = diyStore.editComponent.styleName
selectCouponStyle.value = diyStore.editComponent.style
selectCouponStyle.title = diyStore.editComponent.styleName;
selectCouponStyle.value = diyStore.editComponent.style;
}
const couponStyleList = reactive([
{
url: 'addon/shop/diy/goods_coupon/style-1.png',
title: '风格1',
value: 'style-1',
value: 'style-1'
},
{
url: 'addon/shop/diy/goods_coupon/style-2.png',
title: '风格2',
value: 'style-2',
value: 'style-2'
},
{
url: 'addon/shop/diy/goods_coupon/style-3.png',
title: '风格3',
value: 'style-3',
value: 'style-3'
},
{
url: 'addon/shop/diy/goods_coupon/style-4.png',
title: '风格4',
value: 'style-4',
},
value: 'style-4'
}
])
const changeCouponStyle = (item:any) => {
selectCouponStyle.title = item.title
selectCouponStyle.value = item.value
selectCouponStyle.title = item.title;
selectCouponStyle.value = item.value;
}
const confirmCouponStyle = () => {
diyStore.editComponent.styleName = selectCouponStyle.title
diyStore.editComponent.style = selectCouponStyle.value
diyStore.editComponent.styleName = selectCouponStyle.title;
diyStore.editComponent.style = selectCouponStyle.value;
if(diyStore.editComponent.style == 'style-3'){
if (
diyStore.editComponent.couponTitle &&
diyStore.editComponent.couponTitle.length > 4
) {
diyStore.editComponent.couponTitle =
diyStore.editComponent.couponTitle.substring(0, 4)
}
if (
diyStore.editComponent.couponSubTitle &&
diyStore.editComponent.couponSubTitle.length > 7
) {
diyStore.editComponent.couponSubTitle =
diyStore.editComponent.couponSubTitle.substring(0, 7)
if(diyStore.editComponent.couponTitle && diyStore.editComponent.couponTitle.length > 4){diyStore.editComponent.couponTitle = diyStore.editComponent.couponTitle.substring(0,4)}
if(diyStore.editComponent.couponSubTitle && diyStore.editComponent.couponSubTitle.length > 7){diyStore.editComponent.couponSubTitle = diyStore.editComponent.couponSubTitle.substring(0,7)}
}
}
initStyleFn()
initStyleFn();
showCouponDialog.value = false
}
const initStyleFn = ()=>{
let index = diyStore.editComponent.ignore.indexOf('componentBgColor')
let index = diyStore.editComponent.ignore.indexOf('componentBgColor');
if(diyStore.editComponent.style == 'style-4' && index != -1){
diyStore.editComponent.ignore.splice(index, 1)
diyStore.editComponent.titleColor = '#ffffff'
diyStore.editComponent.subTitleColor = '#ffffff'
diyStore.editComponent.couponItem.moneyColor = '#fa191d'
diyStore.editComponent.couponItem.textColor = '#333333'
diyStore.editComponent.couponItem.subTextColor = '#999999'
diyStore.editComponent.couponItem.bgColor = '#ffffff'
diyStore.editComponent.couponItem.aroundRadius = 10
diyStore.editComponent.componentStartBgColor = '#fa191d'
diyStore.editComponent.ignore.splice(index,1);
diyStore.editComponent.titleColor = "#ffffff";
diyStore.editComponent.subTitleColor = "#ffffff";
diyStore.editComponent.couponItem.moneyColor = "#fa191d";
diyStore.editComponent.couponItem.textColor = "#333333";
diyStore.editComponent.couponItem.subTextColor = "#999999";
diyStore.editComponent.couponItem.bgColor = "#ffffff";
diyStore.editComponent.couponItem.aroundRadius = 10;
diyStore.editComponent.componentStartBgColor = "#fa191d";
}else if(diyStore.editComponent.style != 'style-4' && index == -1){
diyStore.editComponent.ignore.push('componentBgColor')
diyStore.editComponent.ignore.push('componentBgColor');
}
}
initStyleFn()
initStyleFn();
defineExpose({})
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>
<style lang="scss">
.goods-coupon-slider {

324
admin/src/addon/shop/views/diy/components/edit-goods-list.vue

@ -6,33 +6,21 @@
<div class="flex items-center mb-[18px] rounded overflow-hidden">
<span
class="iconfont iconzuoyoutuwenpc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{
'border-[var(--el-color-primary)] text-[var(--el-color-primary)]':
diyStore.editComponent.style == 'style-1',
}"
@click="styleChangeFn('style-1')"
></span>
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-1' }"
@click="styleChangeFn('style-1')"></span>
<span
class="iconfont iconshangxiatuwenpc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{
'border-[var(--el-color-primary)] text-[var(--el-color-primary)]':
diyStore.editComponent.style == 'style-2',
}"
@click="styleChangeFn('style-2')"
></span>
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-2' }"
@click="styleChangeFn('style-2')"></span>
<span
class="iconfont iconliebiaopc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
:class="{
'border-[var(--el-color-primary)] text-[var(--el-color-primary)]':
diyStore.editComponent.style == 'style-3',
}"
@click="styleChangeFn('style-3')"
></span>
:class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-3' }"
@click="styleChangeFn('style-3')"></span>
</div>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectSource') }}</h3>
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('sortWay')">
<el-radio-group v-model="diyStore.editComponent.sortWay">
@ -42,78 +30,33 @@
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group
v-model="diyStore.editComponent.source"
:title="t('goodsSelectPopupSelectGoodsButton')"
>
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('goodsSelectPopupAllGoods') }}</el-radio>
<el-radio label="category">{{ t('selectCategory') }}</el-radio>
<el-radio label="custom">{{
t('manualSelectionSources')
}}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('selectCategory')"
v-if="diyStore.editComponent.source == 'category'"
>
<el-form-item :label="t('selectCategory')" v-if="diyStore.editComponent.source == 'category'">
<div class="flex items-center w-full">
<div class="cursor-pointer ml-auto" @click="categoryShowDialogOpen">
<span class="text-[var(--el-color-primary)]">{{
diyStore.editComponent.goods_category_name
}}</span>
<span class="text-[var(--el-color-primary)]">{{ diyStore.editComponent.goods_category_name }}</span>
<span class="iconfont iconxiangyoujiantou"></span>
</div>
</div>
</el-form-item>
<el-form-item
:label="t('goodsNum')"
v-if="
diyStore.editComponent.source == 'all' ||
diyStore.editComponent.source == 'category'
"
>
<el-slider
class="goods-list-slider"
show-input
v-model="diyStore.editComponent.num"
:min="1"
max="20"
size="small"
/>
<el-form-item :label="t('goodsNum')" v-if="diyStore.editComponent.source == 'all' || diyStore.editComponent.source == 'category'">
<el-slider class="goods-list-slider" show-input v-model="diyStore.editComponent.num" :min="1" max="20" size="small" />
</el-form-item>
<el-form-item
:label="t('customGoods')"
v-if="diyStore.editComponent.source == 'custom'"
>
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="diyStore.editComponent.goods_ids"
:min="1"
:max="99"
/>
<el-form-item :label="t('customGoods')" v-if="diyStore.editComponent.source == 'custom'">
<goods-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.goods_ids" :min="1" :max="99" />
</el-form-item>
</el-form>
<el-dialog
v-model="categoryShowDialog"
:title="t('goodsCategoryTitle')"
width="750px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-table
:data="categoryTable.data"
ref="categoryTableRef"
size="large"
v-loading="categoryTable.loading"
height="450px"
@selection-change="handleSelectionChange"
row-key="category_id"
<el-dialog v-model="categoryShowDialog" :title="t('goodsCategoryTitle')" width="750px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-table :data="categoryTable.data" ref="categoryTableRef" size="large" v-loading="categoryTable.loading"
height="450px" @selection-change="handleSelectionChange" row-key="category_id"
:expand-row-keys="expand_category_ids"
:tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }"
>
:tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }">
<template #empty>
<span>{{ !categoryTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -126,17 +69,10 @@
<el-table-column :label="t('categoryImage')" width="170" align="left">
<template #default="{ row }">
<div class="h-[30px]">
<el-image
class="w-[30px] h-[30px]"
:src="img(row.image)"
fit="contain"
>
<el-image class="w-[30px] h-[30px] " :src="img(row.image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[30px] h-[30px]"
src="@/addon/shop/assets/category_default.png"
/>
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
@ -145,107 +81,56 @@
</el-table-column>
</el-table>
<div class="flex items-center justify-end mt-[15px]">
<el-button type="primary" @click="saveCategoryId">{{
t('confirm')
}}</el-button>
<el-button @click="categoryShowDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="saveCategoryId">{{ t('confirm') }}</el-button>
<el-button @click="categoryShowDialog = false">{{ t('cancel') }}</el-button>
</div>
</el-dialog>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsBuyBtn') }}</h3>
<h3 class="mb-[10px]">{{ t("goodsBuyBtn") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('goodsBtnIsShow')">
<el-switch v-model="diyStore.editComponent.btnStyle.control" />
</el-form-item>
<el-form-item
:label="t('goodsCartIncident')"
v-if="diyStore.editComponent.btnStyle.control"
>
<el-form-item :label="t('goodsCartIncident')" v-if="diyStore.editComponent.btnStyle.control">
<el-radio-group v-model="diyStore.editComponent.btnStyle.cartEvent">
<el-radio label="detail">{{ t('goodsDetail') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('goodsBtnStyle')"
class="!items-center"
v-if="diyStore.editComponent.btnStyle.control"
>
<el-form-item :label="t('goodsBtnStyle')" class="!items-center" v-if="diyStore.editComponent.btnStyle.control">
<div class="flex">
<template v-for="(item,index) in btnStyleList">
<div
v-if="item.isShow == true"
class="cursor-pointer flex items-center justify-center border-[1px] border-solid border-transparent rounded-[6px] py-[5px] px-[8px] mr-[10px]"
:class="{
'!border-[var(--el-color-primary)]':
diyStore.editComponent.btnStyle.style == item.value,
}"
>
<div
v-if="item.type == 'icon'"
:class="[
'nc-iconfont !text-[25px] text-[var(--el-color-primary)]',
item.title,
]"
@click="changeBtnStyle(item)"
></div>
<div
v-if="item.type == 'button'"
class="leading-[1] text-[12px] px-[10px] py-[8px] text-[#fff] rounded-[20px] bg-[var(--el-color-primary)]"
@click="changeBtnStyle(item)"
>
<div v-if=" item.isShow == true" class="cursor-pointer flex items-center justify-center border-[1px] border-solid border-transparent rounded-[6px] py-[5px] px-[8px] mr-[10px]" :class="{'!border-[var(--el-color-primary)]': diyStore.editComponent.btnStyle.style == item.value}">
<div v-if="item.type == 'icon'" :class="['nc-iconfont !text-[25px] text-[var(--el-color-primary)]', item.title]" @click="changeBtnStyle(item)"></div>
<div v-if="item.type == 'button'" class="leading-[1] text-[12px] px-[10px] py-[8px] text-[#fff] rounded-[20px] bg-[var(--el-color-primary)]" @click="changeBtnStyle(item)">
{{item.title}}
</div>
</div>
</template>
</div>
</el-form-item>
<el-form-item
:label="t('goodsBtnText')"
v-if="
diyStore.editComponent.btnStyle.control &&
diyStore.editComponent.btnStyle.style == 'button'
"
>
<el-input
v-model.trim="diyStore.editComponent.btnStyle.text"
:placeholder="t('goodsBtnTextPlaceholder')"
clearable
maxlength="4"
show-word-limit
/>
<el-form-item :label="t('goodsBtnText')" v-if="diyStore.editComponent.btnStyle.control && diyStore.editComponent.btnStyle.style == 'button'">
<el-input v-model.trim="diyStore.editComponent.btnStyle.text" :placeholder="t('goodsBtnTextPlaceholder')" clearable maxlength="4" show-word-limit/>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsShowContent') }}</h3>
<h3 class="mb-[10px]">{{ t("goodsShowContent") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item
:label="t('goodsSelectPopupGoodsName')"
v-if="diyStore.editComponent.goodsNameStyle.isShow"
>
<el-form-item :label="t('goodsSelectPopupGoodsName')" v-if="diyStore.editComponent.goodsNameStyle.isShow">
<el-switch v-model="diyStore.editComponent.goodsNameStyle.control" />
</el-form-item>
<el-form-item
:label="t('goodsPriceColor')"
v-if="diyStore.editComponent.priceStyle.isShow"
>
<el-form-item :label="t('goodsPriceColor')" v-if="diyStore.editComponent.priceStyle.isShow">
<el-switch v-model="diyStore.editComponent.priceStyle.control" />
</el-form-item>
<el-form-item
:label="t('goodsSaleNum')"
v-if="diyStore.editComponent.saleStyle.isShow"
>
<el-form-item :label="t('goodsSaleNum')" v-if="diyStore.editComponent.saleStyle.isShow">
<el-switch v-model="diyStore.editComponent.saleStyle.control" />
</el-form-item>
<el-form-item
:label="t('goodsLabel')"
v-if="diyStore.editComponent.labelStyle.isShow"
>
<el-form-item :label="t('goodsLabel')" v-if="diyStore.editComponent.labelStyle.isShow">
<el-switch v-model="diyStore.editComponent.labelStyle.control" />
</el-form-item>
</el-form>
@ -258,108 +143,47 @@
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsBgColor')">
<el-color-picker
v-model="diyStore.editComponent.elementBgColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.elementBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsNameColor')">
<el-color-picker
v-model="diyStore.editComponent.goodsNameStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
<div class="mr-[20px]"></div>
<el-radio-group
v-model="diyStore.editComponent.goodsNameStyle.fontWeight"
>
<el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsPriceColor')">
<el-color-picker
v-model="diyStore.editComponent.priceStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.priceStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsSaleColor')">
<el-color-picker
v-model="diyStore.editComponent.saleStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider
v-model="diyStore.editComponent.topElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider
v-model="diyStore.editComponent.bottomElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<div
class="edit-attr-item-wrap"
v-if="diyStore.editComponent.btnStyle.control"
>
<h3 class="mb-[10px]">{{ t('goodsBuyBtn') }}</h3>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.btnStyle.control">
<h3 class="mb-[10px]">{{ t("goodsBuyBtn") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item
:label="t('goodsIsBold')"
v-if="diyStore.editComponent.btnStyle.style == 'button'"
>
<el-form-item :label="t('goodsIsBold')" v-if="diyStore.editComponent.btnStyle.style == 'button'">
<el-switch v-model="diyStore.editComponent.btnStyle.fontWeight" />
</el-form-item>
<el-form-item :label="t('goodsTextColor')">
<el-color-picker
v-model="diyStore.editComponent.btnStyle.textColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.btnStyle.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('listFrameColor')">
<el-color-picker
v-model="diyStore.editComponent.btnStyle.startBgColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<icon
name="iconfont iconmap-connect"
size="20px"
class="block !text-gray-400 mx-[5px]"
/>
<el-color-picker
v-model="diyStore.editComponent.btnStyle.endBgColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.btnStyle.startBgColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="diyStore.editComponent.btnStyle.endBgColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item
:label="t('goodsRounded')"
v-if="diyStore.editComponent.btnStyle.style == 'button'"
>
<el-slider
v-model="diyStore.editComponent.btnStyle.aroundRadius"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-form-item :label="t('goodsRounded')" v-if="diyStore.editComponent.btnStyle.style == 'button'">
<el-slider v-model="diyStore.editComponent.btnStyle.aroundRadius" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
@ -409,32 +233,33 @@ onMounted(() => {
loadCategoryList()
})
const styleChangeFn = (style)=>{
btnStyleList.forEach((item,index,arr)=>{
if (item.type == 'button') {
if (style == 'style-3') {
item.isShow = false
if(item.type == "button"){
if(style == "style-3"){
item.isShow = false;
}else{
item.isShow = true
item.isShow = true;
}
}
})
if (style == 'style-3') {
if(style == "style-3"){
diyStore.editComponent.btnStyle.style = btnStyleList[1].value
}else{
diyStore.editComponent.btnStyle.style = btnStyleList[0].value
}
if (style == 'style-3') {
diyStore.editComponent.saleStyle.isShow = false
diyStore.editComponent.labelStyle.isShow = false
if(style == "style-3"){
diyStore.editComponent.saleStyle.isShow = false;
diyStore.editComponent.labelStyle.isShow = false;
}else{
diyStore.editComponent.saleStyle.isShow = true
diyStore.editComponent.labelStyle.isShow = true
diyStore.editComponent.saleStyle.isShow = true;
diyStore.editComponent.labelStyle.isShow = true;
}
diyStore.editComponent.style = style
diyStore.editComponent.style = style;
}
const btnStyleList = reactive([
@ -442,20 +267,20 @@ const btnStyleList = reactive([
isShow: true,
type: 'button',
title: diyStore.editComponent.btnStyle.text,
value: 'button',
value: 'button'
},
{
isShow: true,
type: 'icon',
title: 'nc-icon-jiahaoV6xx',
value: 'nc-icon-jiahaoV6xx',
value: 'nc-icon-jiahaoV6xx'
},
{
isShow: true,
type: 'icon',
title: 'nc-icon-gouwuche1',
value: 'nc-icon-gouwuche1',
},
value: 'nc-icon-gouwuche1'
}
])
const changeBtnStyle = (item:any) => {
@ -470,12 +295,10 @@ let currCategoryData: any = null
const loadCategoryList = () => {
categoryTable.loading = true
getCategoryTree()
.then((res) => {
getCategoryTree().then(res => {
categoryTable.loading = false
categoryTable.data = res.data
})
.catch(() => {
}).catch(() => {
categoryTable.loading = false
})
}
@ -491,7 +314,7 @@ const handleSelectionChange = (val: string | any[]) => {
const saveCategoryId = () => {
diyStore.editComponent.goods_category = currCategoryData.category_id
diyStore.editComponent.goods_category_name = currCategoryData.category_name
diyStore.editComponent.goods_category_name = currCategoryData.category_name;
categoryShowDialog.value = false
}
@ -521,6 +344,7 @@ const setRowSelection = () => {
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

442
admin/src/addon/shop/views/diy/components/edit-many-goods-list.vue

@ -12,17 +12,8 @@
<el-radio label="style-4">{{ t('headStyle4') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('manyGoodsListAroundRadius')"
v-show="diyStore.editComponent.headStyle == 'style-3'"
>
<el-slider
v-model="diyStore.editComponent.aroundRadius"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-form-item :label="t('manyGoodsListAroundRadius')" v-show="diyStore.editComponent.headStyle == 'style-3'">
<el-slider v-model="diyStore.editComponent.aroundRadius" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
@ -37,41 +28,21 @@
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsNum')">
<el-slider
show-input
class="diy-nav-slider"
v-model="diyStore.editComponent.num"
:min="1"
max="20"
size="small"
/>
<el-slider show-input class="diy-nav-slider" v-model="diyStore.editComponent.num" :min="1" max="20" size="small" />
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('manyGoodsListCategorySet') }}</h3>
<h3 class="mb-[10px]">{{ t("manyGoodsListCategorySet") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('dataSources')">
<el-radio-group v-model="diyStore.editComponent.source">
<el-radio label="custom">{{
t('manyGoodsListSourceDiy')
}}</el-radio>
<el-radio label="goods_category">{{
t('manyGoodsListSourceCategory')
}}</el-radio>
<el-radio label="custom">{{ t('manyGoodsListSourceDiy') }}</el-radio>
<el-radio label="goods_category">{{ t('manyGoodsListSourceCategory') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('goodsCategoryTitle')"
v-show="diyStore.editComponent.source == 'goods_category'"
>
<el-input
v-model.trim="diyStore.editComponent.goods_category_name"
:placeholder="t('selectCategory')"
readonly
class="select-diy-page-input"
@click="firstCategoryShowDialogOpen()"
>
<el-form-item :label="t('goodsCategoryTitle')" v-show="diyStore.editComponent.source == 'goods_category'">
<el-input v-model.trim="diyStore.editComponent.goods_category_name" :placeholder="t('selectCategory')" readonly class="select-diy-page-input" @click="firstCategoryShowDialogOpen()">
<template #suffix>
<div @click.stop="clearCategory">
<el-icon v-if="diyStore.editComponent.goods_category_name">
@ -85,121 +56,55 @@
</el-input>
</el-form-item>
<div v-show="diyStore.editComponent.source == 'custom'">
<p class="text-sm text-gray-400 mb-[10px]">
{{ t('dragMouseAdjustOrder') }}
</p>
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
<div ref="goodsBoxRef">
<div
v-for="(item, index) in diyStore.editComponent.list"
:key="item.id"
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]"
>
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('manyGoodsListCategoryName')">
<el-input
v-model.trim="item.title"
clearable
maxlength="4"
show-word-limit
/>
<el-input v-model.trim="item.title" clearable maxlength="4" show-word-limit/>
</el-form-item>
<el-form-item
:label="t('manyGoodsListSubTitle')"
v-show="diyStore.editComponent.headStyle == 'style-1'"
>
<el-input
v-model.trim="item.desc"
clearable
maxlength="5"
show-word-limit
/>
<el-form-item :label="t('manyGoodsListSubTitle')" v-show="diyStore.editComponent.headStyle == 'style-1'">
<el-input v-model.trim="item.desc" clearable maxlength="5" show-word-limit/>
</el-form-item>
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group v-model="item.source">
<el-radio label="all">{{
t('goodsSelectPopupAllGoods')
}}</el-radio>
<el-radio label="category">{{
t('selectCategory')
}}</el-radio>
<el-radio label="custom">{{
t('manualSelectionSources')
}}</el-radio>
<el-radio label="all">{{ t('goodsSelectPopupAllGoods') }}</el-radio>
<el-radio label="category">{{ t('selectCategory') }}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('selectCategory')"
v-if="item.source == 'category'"
>
<el-form-item :label="t('selectCategory')" v-if="item.source == 'category'">
<div class="flex items-center w-full">
<div
class="cursor-pointer ml-auto"
@click="categoryShowDialogOpen(index)"
>
<span class="text-[var(--el-color-primary)]">{{
item.goods_category_name
}}</span>
<div class="cursor-pointer ml-auto" @click="categoryShowDialogOpen(index)">
<span class="text-[var(--el-color-primary)]">{{ item.goods_category_name }}</span>
<span class="iconfont iconxiangyoujiantou"></span>
</div>
</div>
</el-form-item>
<el-form-item
:label="t('customGoods')"
v-if="item.source == 'custom'"
>
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="item.goods_ids"
:min="1"
:max="99"
/>
<el-form-item :label="t('customGoods')" v-if="item.source == 'custom'">
<goods-select-popup ref="goodsSelectPopupRef" v-model="item.goods_ids" :min="1" :max="99" />
</el-form-item>
<el-form-item
:label="t('image')"
v-show="diyStore.editComponent.headStyle == 'style-3'"
>
<el-form-item :label="t('image')" v-show="diyStore.editComponent.headStyle == 'style-3'">
<upload-image v-model="item.imageUrl" :limit="1" />
</el-form-item>
<div
class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
v-show="diyStore.editComponent.list.length > 1"
@click="diyStore.editComponent.list.splice(index, 1)"
>
<icon
name="element CircleCloseFilled"
color="#bbb"
size="20px"
/>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.list.length > 1" @click="diyStore.editComponent.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px"/>
</div>
</div>
</div>
<el-button class="w-full" @click="addItem">{{
t('manyGoodsLisAddItem')
}}</el-button>
<el-button class="w-full" @click="addItem">{{ t('manyGoodsLisAddItem') }}</el-button>
</div>
</el-form>
<!-- 选择一级商品分类弹出框 -->
<el-dialog
v-model="firstCategoryShowDialog"
:title="t('goodsCategoryTitle')"
width="750px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-table
:data="firstCategoryTable.data"
ref="firstCategoryTableRef"
size="large"
v-loading="firstCategoryTable.loading"
height="450px"
@current-change="handleCurrentCategoryChange"
row-key="category_id"
highlight-current-row
>
<el-dialog v-model="firstCategoryShowDialog" :title="t('goodsCategoryTitle')" width="750px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-table :data="firstCategoryTable.data" ref="firstCategoryTableRef" size="large" v-loading="firstCategoryTable.loading" height="450px" @current-change="handleCurrentCategoryChange" row-key="category_id" highlight-current-row>
<template #empty>
<span>{{ !firstCategoryTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -211,17 +116,10 @@
<el-table-column :label="t('categoryImage')" width="170" align="left">
<template #default="{ row }">
<div class="h-[30px]">
<el-image
class="w-[30px] h-[30px]"
:src="img(row.image)"
fit="contain"
>
<el-image class="w-[30px] h-[30px] " :src="img(row.image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[30px] h-[30px]"
src="@/addon/shop/assets/category_default.png"
/>
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
@ -230,34 +128,16 @@
</el-table-column>
</el-table>
<div class="flex items-center justify-end mt-[15px]">
<el-button type="primary" @click="saveFirstCategoryId">{{
t('confirm')
}}</el-button>
<el-button @click="firstCategoryShowDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="saveFirstCategoryId">{{ t('confirm') }}</el-button>
<el-button @click="firstCategoryShowDialog = false">{{ t('cancel') }}</el-button>
</div>
</el-dialog>
<el-dialog
v-model="categoryShowDialog"
:title="t('goodsCategoryTitle')"
width="750px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-table
:data="categoryTable.data"
ref="categoryTableRef"
size="large"
v-loading="categoryTable.loading"
height="490px"
@selection-change="handleSelectionChange"
row-key="category_id"
<el-dialog v-model="categoryShowDialog" :title="t('goodsCategoryTitle')" width="750px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-table :data="categoryTable.data" ref="categoryTableRef" size="large" v-loading="categoryTable.loading"
height="490px" @selection-change="handleSelectionChange" row-key="category_id"
:expand-row-keys="expand_category_ids"
:tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }"
>
:tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }">
<template #empty>
<span>{{ !categoryTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -270,17 +150,10 @@
<el-table-column :label="t('categoryImage')" width="170" align="left">
<template #default="{ row }">
<div class="h-[30px]">
<el-image
class="w-[30px] h-[30px]"
:src="img(row.image)"
fit="contain"
>
<el-image class="w-[30px] h-[30px] " :src="img(row.image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[30px] h-[30px]"
src="@/addon/shop/assets/category_default.png"
/>
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
@ -289,107 +162,56 @@
</el-table-column>
</el-table>
<div class="flex items-center justify-end mt-[15px]">
<el-button type="primary" @click="saveCategoryId">{{
t('confirm')
}}</el-button>
<el-button @click="categoryShowDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="saveCategoryId">{{ t('confirm') }}</el-button>
<el-button @click="categoryShowDialog = false">{{ t('cancel') }}</el-button>
</div>
</el-dialog>
</div>
<div class="edit-attr-item-wrap mt-[20px]">
<h3 class="mb-[10px]">{{ t('goodsBuyBtn') }}</h3>
<h3 class="mb-[10px]">{{ t("goodsBuyBtn") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('goodsBtnIsShow')">
<el-switch v-model="diyStore.editComponent.btnStyle.control" />
</el-form-item>
<el-form-item
:label="t('goodsCartIncident')"
v-if="diyStore.editComponent.btnStyle.control"
>
<el-form-item :label="t('goodsCartIncident')" v-if="diyStore.editComponent.btnStyle.control">
<el-radio-group v-model="diyStore.editComponent.btnStyle.cartEvent">
<el-radio label="detail">{{ t('goodsDetail') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('goodsBtnStyle')"
class="!items-center"
v-if="diyStore.editComponent.btnStyle.control"
>
<el-form-item :label="t('goodsBtnStyle')" class="!items-center" v-if="diyStore.editComponent.btnStyle.control">
<div class="flex">
<template v-for="(item,index) in btnStyleList">
<div
v-if="item.isShow == true"
class="cursor-pointer flex items-center justify-center border-[1px] border-solid border-transparent rounded-[6px] py-[5px] px-[8px] mr-[10px]"
:class="{
'!border-[var(--el-color-primary)]':
diyStore.editComponent.btnStyle.style == item.value,
}"
>
<div
v-if="item.type == 'icon'"
:class="[
'nc-iconfont !text-[25px] text-[var(--el-color-primary)]',
item.title,
]"
@click="changeBtnStyle(item)"
></div>
<div
v-if="item.type == 'button'"
class="leading-[1] text-[12px] px-[10px] py-[8px] text-[#fff] rounded-[20px] bg-[var(--el-color-primary)]"
@click="changeBtnStyle(item)"
>
<div v-if=" item.isShow == true" class="cursor-pointer flex items-center justify-center border-[1px] border-solid border-transparent rounded-[6px] py-[5px] px-[8px] mr-[10px]" :class="{'!border-[var(--el-color-primary)]': diyStore.editComponent.btnStyle.style == item.value}">
<div v-if="item.type == 'icon'" :class="['nc-iconfont !text-[25px] text-[var(--el-color-primary)]', item.title]" @click="changeBtnStyle(item)"></div>
<div v-if="item.type == 'button'" class="leading-[1] text-[12px] px-[10px] py-[8px] text-[#fff] rounded-[20px] bg-[var(--el-color-primary)]" @click="changeBtnStyle(item)">
{{item.title}}
</div>
</div>
</template>
</div>
</el-form-item>
<el-form-item
:label="t('goodsBtnText')"
v-if="
diyStore.editComponent.btnStyle.control &&
diyStore.editComponent.btnStyle.style == 'button'
"
>
<el-input
v-model.trim="diyStore.editComponent.btnStyle.text"
:placeholder="t('goodsBtnTextPlaceholder')"
clearable
maxlength="4"
show-word-limit
/>
<el-form-item :label="t('goodsBtnText')" v-if="diyStore.editComponent.btnStyle.control && diyStore.editComponent.btnStyle.style == 'button'">
<el-input v-model.trim="diyStore.editComponent.btnStyle.text" :placeholder="t('goodsBtnTextPlaceholder')" clearable maxlength="4" show-word-limit/>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsShowContent') }}</h3>
<h3 class="mb-[10px]">{{ t("goodsShowContent") }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item
:label="t('goodsSelectPopupGoodsName')"
v-if="diyStore.editComponent.goodsNameStyle.isShow"
>
<el-form-item :label="t('goodsSelectPopupGoodsName')" v-if="diyStore.editComponent.goodsNameStyle.isShow">
<el-switch v-model="diyStore.editComponent.goodsNameStyle.control" />
</el-form-item>
<el-form-item
:label="t('goodsPriceColor')"
v-if="diyStore.editComponent.priceStyle.isShow"
>
<el-form-item :label="t('goodsPriceColor')" v-if="diyStore.editComponent.priceStyle.isShow">
<el-switch v-model="diyStore.editComponent.priceStyle.control" />
</el-form-item>
<el-form-item
:label="t('goodsSaleNum')"
v-if="diyStore.editComponent.saleStyle.isShow"
>
<el-form-item :label="t('goodsSaleNum')" v-if="diyStore.editComponent.saleStyle.isShow">
<el-switch v-model="diyStore.editComponent.saleStyle.control" />
</el-form-item>
<el-form-item
:label="t('goodsLabel')"
v-if="diyStore.editComponent.labelStyle.isShow"
>
<el-form-item :label="t('goodsLabel')" v-if="diyStore.editComponent.labelStyle.isShow">
<el-switch v-model="diyStore.editComponent.labelStyle.control" />
</el-form-item>
</el-form>
@ -398,94 +220,46 @@
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsBgColor')">
<el-color-picker
v-model="diyStore.editComponent.elementBgColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.elementBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsNameColor')">
<el-color-picker
v-model="diyStore.editComponent.goodsNameStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
<div class="mr-[20px]"></div>
<el-radio-group
v-model="diyStore.editComponent.goodsNameStyle.fontWeight"
>
<el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsPriceColor')">
<el-color-picker
v-model="diyStore.editComponent.priceStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.priceStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsSaleColor')">
<el-color-picker
v-model="diyStore.editComponent.saleStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider
v-model="diyStore.editComponent.topElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider
v-model="diyStore.editComponent.bottomElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<div
class="edit-attr-item-wrap"
v-if="diyStore.editComponent.btnStyle.control"
>
<h3 class="mb-[10px]">{{ t('goodsBuyBtn') }}</h3>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent.btnStyle.control">
<h3 class="mb-[10px]">{{ t("goodsBuyBtn") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsTextColor')">
<el-color-picker
v-model="diyStore.editComponent.btnStyle.textColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.btnStyle.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('listFrameColor')">
<el-color-picker
v-model="diyStore.editComponent.btnStyle.startBgColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<icon
name="iconfont iconmap-connect"
size="20px"
class="block !text-gray-400 mx-[5px]"
/>
<el-color-picker
v-model="diyStore.editComponent.btnStyle.endBgColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.btnStyle.startBgColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="diyStore.editComponent.btnStyle.endBgColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
@ -514,6 +288,7 @@ diyStore.editComponent.verify = (index: number) => {
const res = { code: true, message: '' }
if(diyStore.value[index].source == 'custom') {
diyStore.value[index].list.forEach((item: any) => {
if (item.source === 'category') {
if (item.goods_category == '') {
@ -527,7 +302,7 @@ diyStore.editComponent.verify = (index: number) => {
res.message = t('goodsPlaceholder')
}
}
})
});
}else if(diyStore.value[index].source == 'goods_category') {
if (diyStore.value[index].goods_category == '') {
res.code = false
@ -550,8 +325,8 @@ const firstCategoryTable = reactive({
loading: true,
data: [],
searchParam: {
level: 1,
},
level: 1
}
})
const firstCategoryTableRef = ref<InstanceType<typeof ElTable>>()
@ -563,20 +338,18 @@ const loadCategoryList = () => {
firstCategoryTable.loading = true
getCategoryList({
...firstCategoryTable.searchParam,
})
.then((res) => {
...firstCategoryTable.searchParam
}).then(res => {
firstCategoryTable.loading = false
firstCategoryTable.data = res.data
})
.catch(() => {
}).catch(() => {
firstCategoryTable.loading = false
})
}
const saveFirstCategoryId = () => {
diyStore.editComponent.goods_category = currFirstCategory.category_id
diyStore.editComponent.goods_category_name = currFirstCategory.category_name
diyStore.editComponent.goods_category_name = currFirstCategory.category_name;
firstCategoryShowDialog.value = false
}
@ -589,27 +362,28 @@ const firstCategoryShowDialogOpen = () => {
}
}
const btnStyleList = reactive([
{
isShow: true,
type: 'button',
title: diyStore.editComponent.btnStyle.text,
value: 'button',
value: 'button'
},
{
isShow: true,
type: 'icon',
title: 'nc-icon-jiahaoV6xx',
value: 'nc-icon-jiahaoV6xx',
value: 'nc-icon-jiahaoV6xx'
},
{
isShow: true,
type: 'icon',
title: 'nc-icon-gouwuche1',
value: 'nc-icon-gouwuche1',
},
value: 'nc-icon-gouwuche1'
}
])
diyStore.editComponent.btnStyle.style = 'nc-icon-jiahaoV6xx'
diyStore.editComponent.btnStyle.style = 'nc-icon-jiahaoV6xx';
const changeBtnStyle = (item:any) => {
diyStore.editComponent.btnStyle.style = item.value
@ -617,7 +391,7 @@ const changeBtnStyle = (item: any) => {
const clearCategory = ()=> {
diyStore.editComponent.goods_category = ''
diyStore.editComponent.goods_category_name = ''
diyStore.editComponent.goods_category_name = '';
}
//
@ -633,7 +407,7 @@ const goodsBoxRef = ref()
const categoryTable = reactive({
loading: true,
data: [],
data: []
})
onMounted(() => {
@ -645,16 +419,16 @@ onMounted(() => {
const sortable = Sortable.create(goodsBoxRef.value, {
group: 'item-wrap',
animation: 200,
onEnd: (event) => {
onEnd: event => {
const temp = diyStore.editComponent.list[event.oldIndex!]
diyStore.editComponent.list.splice(event.oldIndex!, 1)
diyStore.editComponent.list.splice(event.newIndex!, 0, temp)
sortable.sort(
range(diyStore.editComponent.list.length).map((value) => {
range(diyStore.editComponent.list.length).map(value => {
return value.toString()
})
)
},
}
})
})
})
@ -668,18 +442,16 @@ let currCategoryData: any = null
const loadCategoryTree = () => {
categoryTable.loading = true
getCategoryTree()
.then((res) => {
getCategoryTree().then(res => {
categoryTable.loading = false
categoryTable.data = res.data
})
.catch(() => {
}).catch(() => {
categoryTable.loading = false
})
}
//
let selectIndex = 0 //
let selectIndex = 0; //
const handleSelectionChange = (val: string | any[]) => {
let data = ''
if (val) data = val[val.length - 1]
@ -689,15 +461,13 @@ const handleSelectionChange = (val: string | any[]) => {
}
const saveCategoryId = () => {
diyStore.editComponent.list[selectIndex].goods_category =
currCategoryData.category_id
diyStore.editComponent.list[selectIndex].goods_category_name =
currCategoryData.category_name
diyStore.editComponent.list[selectIndex].goods_category = currCategoryData.category_id
diyStore.editComponent.list[selectIndex].goods_category_name = currCategoryData.category_name;
categoryShowDialog.value = false
}
const categoryShowDialogOpen = (index: any) => {
selectIndex = index
selectIndex = index;
categoryShowDialog.value = true
nextTick(()=>{
@ -710,16 +480,11 @@ const expand_category_ids = ref<Array<any>>([])
const setRowSelection = ()=>{
expand_category_ids.value = []
categoryTable.data.forEach((el:any)=>{
if (
diyStore.editComponent.list[selectIndex].goods_category == el.category_id
) {
if(diyStore.editComponent.list[selectIndex].goods_category == el.category_id){
categoryTableRef.value!.toggleRowSelection(el, true)
}else if(el.child_list&&el.child_list.length){
el.child_list.forEach((v:any)=>{
if (
diyStore.editComponent.list[selectIndex].goods_category ==
v.category_id
) {
if(diyStore.editComponent.list[selectIndex].goods_category == v.category_id){
expand_category_ids.value.push(el.category_id.toString())
categoryTableRef.value!.toggleRowSelection(v, true)
}
@ -731,17 +496,18 @@ const setRowSelection = () => {
const addItem = () => {
diyStore.editComponent.list.push({
id: diyStore.generateRandom(),
title: '分类',
desc: '分类描述',
source: 'all',
title : "分类",
desc : "分类描述",
source : "all",
goods_category : '',
goods_category_name : '请选择',
goods_ids : [],
imageUrl: '',
imageUrl:''
})
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

83
admin/src/addon/shop/views/diy/components/edit-shop-exchange-goods.vue

@ -20,7 +20,7 @@
</div>
</div> -->
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectSource') }}</h3>
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('sortWay')">
<el-radio-group v-model="diyStore.editComponent.sortWay">
@ -30,16 +30,9 @@
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group
v-model="diyStore.editComponent.source"
:title="t('goodsSelectPopupSelectGoodsButton')"
>
<el-radio label="all">{{
t('goodsSelectPopupAllGoods')
}}</el-radio>
<el-radio label="custom">{{
t('manualSelectionSources')
}}</el-radio>
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('goodsSelectPopupAllGoods') }}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- <el-form-item :label="t('selectCategory')" v-if="diyStore.editComponent.source == 'category'">
@ -56,34 +49,17 @@
<span class="ml-[15px]">{{ diyStore.editComponent.num }}</span>
</div>
</el-form-item> -->
<el-form-item
:label="t('customGoods')"
v-if="diyStore.editComponent.source == 'custom'"
>
<el-button
type="primary"
@click="
goodsSelectPopupRef.show(diyStore.editComponent.goods_ids)
"
>{{ t('goodsSelectPopupSelectGoodsButton') }}</el-button
>
<div
class="inline-block ml-[10px] text-[14px]"
v-show="diyStore.editComponent.goods_ids.length"
>
<el-form-item :label="t('customGoods')" v-if="diyStore.editComponent.source == 'custom'">
<el-button type="primary" @click="goodsSelectPopupRef.show(diyStore.editComponent.goods_ids)">{{ t('goodsSelectPopupSelectGoodsButton') }}</el-button>
<div class="inline-block ml-[10px] text-[14px]" v-show="diyStore.editComponent.goods_ids.length">
<span>{{ t('goodsSelectPopupSelect') }}</span>
<span class="text-primary mx-[2px]">{{
diyStore.editComponent.goods_ids.length
}}</span>
<span class="text-primary mx-[2px]">{{ diyStore.editComponent.goods_ids.length }}</span>
<span>{{ t('goodsSelectPopupPiece') }}</span>
</div>
<goods-select-popup
ref="goodsSelectPopupRef"
:min="1"
@select="goodsSelect"
/>
<goods-select-popup ref="goodsSelectPopupRef" :min="1" @select="goodsSelect"/>
</el-form-item>
</el-form>
</div>
</div>
@ -93,50 +69,24 @@
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsNameColor')">
<el-color-picker
v-model="diyStore.editComponent.goodsNameStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
<div class="mr-[20px]"></div>
<el-radio-group
v-model="diyStore.editComponent.goodsNameStyle.fontWeight"
>
<el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsNumColor')">
<el-color-picker
v-model="diyStore.editComponent.saleStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsPriceColor')">
<el-color-picker
v-model="diyStore.editComponent.priceStyle.mainColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.priceStyle.mainColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider
v-model="diyStore.editComponent.topElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider
v-model="diyStore.editComponent.bottomElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
@ -181,6 +131,7 @@ const goodsSelect = (val: any) => {
diyStore.editComponent.goods_ids = val.map((el:any)=>el.id)
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

1
admin/src/addon/shop/views/diy/components/edit-shop-exchange-info.vue

@ -32,6 +32,7 @@ diyStore.editComponent.verify = (index: number) => {
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

115
admin/src/addon/shop/views/diy/components/edit-shop-goods-ranking.vue

@ -4,32 +4,17 @@
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('activeCubeBlockContent') }}</h3>
<el-form label-width="85px" class="px-[10px]">
<div ref="blockBoxRef">
<div
v-for="(item, index) in diyStore.editComponent.list"
:key="item.id"
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]"
>
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('bgImage')">
<upload-image v-model="item.bgUrl" :limit="1" />
</el-form-item>
<el-form-item :label="t('listFrameColor')">
<el-color-picker
v-model="item.listFrame.startColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<icon
name="iconfont iconmap-connect"
size="20px"
class="block !text-gray-400 mx-[5px]"
/>
<el-color-picker
v-model="item.listFrame.endColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="item.listFrame.startColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="item.listFrame.endColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('rankingTitleIcon')">
@ -37,38 +22,18 @@
</el-form-item>
<el-form-item :label="t('rankName')">
<el-input
v-model="item.text"
clearable
:placeholder="t('rankNamePlaceholder')"
maxlength="8"
show-word-limit
/>
<el-input v-model="item.text" clearable :placeholder="t('rankNamePlaceholder')" maxlength="8" show-word-limit />
</el-form-item>
<el-form-item :label="t('rankTextColor')">
<el-color-picker
v-model="item.textColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="item.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('rankingSubTitle')">
<el-input
v-model.trim="item.subTitle.text"
:placeholder="t('activeCubeSubTitlePlaceholder')"
clearable
maxlength="6"
show-word-limit
/>
<el-input v-model.trim="item.subTitle.text" :placeholder="t('activeCubeSubTitlePlaceholder')" clearable maxlength="6" show-word-limit />
</el-form-item>
<el-form-item :label="t('rankingSubTitleTextColor')">
<el-color-picker
v-model="item.subTitle.textColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="item.subTitle.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('rankingSubTitleLink')">
@ -76,42 +41,22 @@
</el-form-item>
<el-form-item :label="t('rankSelectPopupSelectRankButton')">
<el-radio-group
v-model="item.source"
:title="t('rankSelectPopupSelectRankButton')"
>
<el-radio-group v-model="item.source" :title="t('rankSelectPopupSelectRankButton')">
<el-radio label="default">{{ t('defaultSources') }}</el-radio>
<el-radio label="custom">{{
t('manualSelectionSources')
}}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('customGoods')"
v-if="item.source == 'custom'"
>
<rank-select-popup
ref="goodsSelectPopupRef"
v-model="diyStore.editComponent.list[index].rankIds"
:max="1"
/>
<el-form-item :label="t('customGoods')" v-if="item.source == 'custom'">
<rank-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.list[index].rankIds" :max="1" />
</el-form-item>
<div
class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
v-show="diyStore.editComponent.list.length > 1"
@click="diyStore.editComponent.list.splice(index, 1)"
>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.list.length > 1" @click="diyStore.editComponent.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px" />
</div>
</div>
</div>
<el-button
v-show="diyStore.editComponent.list.length < 10"
class="w-full"
@click="addItem"
>{{ t('activeCubeAddItem') }}</el-button
>
<el-button v-show="diyStore.editComponent.list.length < 10" class="w-full" @click="addItem">{{ t('activeCubeAddItem') }}</el-button>
</el-form>
</div>
</div>
@ -122,22 +67,10 @@
<h3 class="mb-[10px]">{{ t('rankingStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('topRounded')">
<el-slider
v-model="diyStore.editComponent.topElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider
v-model="diyStore.editComponent.bottomElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
@ -167,7 +100,7 @@ diyStore.editComponent.verify = (index: number) => {
res.message = t('请选择榜单')
}
}
})
});
return res
}
@ -176,21 +109,21 @@ const addItem = () => {
id: diyStore.generateRandom(),
bgUrl: '',
text: '榜单名称',
textColor: '#FFFFFF',
imgUrl: '',
textColor: "#FFFFFF",
imgUrl: "",
subTitle: {
text: '查看更多',
textColor: '#FFFFFF',
link: {
name: '',
},
name: ''
}
},
listFrame: {
startColor: '#FEA715',
endColor: '#FE1E00',
},
source: 'default',
rankIds: [],
rankIds: []
})
}

174
admin/src/addon/shop/views/diy/components/edit-shop-goods-recommend.vue

@ -2,29 +2,16 @@
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectSource') }}</h3>
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group
v-model="diyStore.editComponent.source"
:title="t('goodsSelectPopupSelectGoodsButton')"
>
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('defaultGoodsSelect') }}</el-radio>
<el-radio label="custom">{{
t('manualSelectionSources')
}}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('customGoods')"
v-if="diyStore.editComponent.source == 'custom'"
>
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="diyStore.editComponent.goods_ids"
:min="diyStore.editComponent.list.length"
:max="diyStore.editComponent.list.length"
/>
<el-form-item :label="t('customGoods')" v-if="diyStore.editComponent.source == 'custom'">
<goods-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.goods_ids" :min="diyStore.editComponent.list.length" :max="diyStore.editComponent.list.length" />
</el-form-item>
</el-form>
</div>
@ -32,105 +19,40 @@
<div class="edit-attr-item-wrap">
<el-form label-width="120px" class="px-[10px]">
<h3 class="mb-[10px]">{{ t('activeCubeBlockContent') }}</h3>
<p class="text-sm text-gray-400 mb-[10px]">
{{ t('dragMouseAdjustOrder') }}
</p>
<p class="text-sm text-gray-400 mb-[10px]">{{ t('dragMouseAdjustOrder') }}</p>
<div ref="blockBoxRef">
<div
v-for="(item, index) in diyStore.editComponent.list"
:key="item.id"
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]"
>
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('activeCubeTitle')">
<el-input
v-model.trim="item.title.text"
:placeholder="t('activeCubeTitlePlaceholder')"
clearable
maxlength="4"
show-word-limit
/>
<el-input v-model.trim="item.title.text" :placeholder="t('activeCubeTitlePlaceholder')" clearable maxlength="4" show-word-limit/>
</el-form-item>
<el-form-item :label="t('shopGoodsRecommendComponentTag')">
<el-input
v-model.trim="item.moreTitle.text"
:placeholder="t('shopGoodsRecommendComponentTagPlaceholder')"
clearable
maxlength="2"
show-word-limit
/>
<el-input v-model.trim="item.moreTitle.text" :placeholder="t('shopGoodsRecommendComponentTagPlaceholder')" clearable maxlength="2" show-word-limit/>
</el-form-item>
<el-form-item :label="t('activeCubeButton')">
<el-input
v-model.trim="item.button.text"
:placeholder="t('activeCubeButtonPlaceholder')"
clearable
maxlength="2"
show-word-limit
/>
<el-input v-model.trim="item.button.text" :placeholder="t('activeCubeButtonPlaceholder')" clearable maxlength="2" show-word-limit/>
</el-form-item>
<el-form-item :label="t('activeCubeSubTitleTextColor')">
<el-color-picker
v-model="item.title.textColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="item.title.textColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('shopGoodsRecommendComponentTagcolor')">
<el-color-picker
v-model="item.moreTitle.startColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<icon
name="iconfont iconmap-connect"
size="20px"
class="block !text-gray-400 mx-[5px]"
/>
<el-color-picker
v-model="item.moreTitle.endColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="item.moreTitle.startColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="item.moreTitle.endColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('activeCubeButtonColor')">
<el-color-picker
v-model="item.button.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="item.button.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('activeListFrameColor')">
<el-color-picker
v-model="item.listFrame.startColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<icon
name="iconfont iconmap-connect"
size="20px"
class="block !text-gray-400 mx-[5px]"
/>
<el-color-picker
v-model="item.listFrame.endColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="item.listFrame.startColor" show-alpha :predefine="diyStore.predefineColors" />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="item.listFrame.endColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<div
class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
v-show="diyStore.editComponent.list.length > 1"
@click="deleteTempFn(index)"
>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.list.length > 1" @click="deleteTempFn(index)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px"/>
</div>
</div>
</div>
<el-button
v-show="diyStore.editComponent.list.length < 10"
class="w-full"
@click="addItem"
>{{ t('activeCubeAddItem') }}</el-button
>
<el-button v-show="diyStore.editComponent.list.length < 10" class="w-full" @click="addItem">{{ t('activeCubeAddItem') }}</el-button>
</el-form>
</div>
</div>
@ -141,29 +63,13 @@
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsPriceColor')">
<el-color-picker
v-model="diyStore.editComponent.priceStyle.mainColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.priceStyle.mainColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider
v-model="diyStore.editComponent.topElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider
v-model="diyStore.editComponent.bottomElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
@ -205,10 +111,7 @@ diyStore.editComponent.verify = (index: number) => {
}
})
if (
diyStore.value[index].source == 'custom' &&
diyStore.value[index].goods_ids.length < diyStore.value[index].list.length
) {
if (diyStore.value[index].source == 'custom' && diyStore.value[index].goods_ids.length < diyStore.value[index].list.length) {
res.code = false
res.message = t('goodsPlaceholder')
return res
@ -227,21 +130,21 @@ onMounted(() => {
const sortable = Sortable.create(blockBoxRef.value, {
group: 'item-wrap',
animation: 200,
onEnd: (event) => {
onEnd: event => {
const temp = diyStore.editComponent.list[event.oldIndex!]
diyStore.editComponent.list.splice(event.oldIndex!, 1)
diyStore.editComponent.list.splice(event.newIndex!, 0, temp)
sortable.sort(
range(diyStore.editComponent.list.length).map((value) => {
range(diyStore.editComponent.list.length).map(value => {
return value.toString()
})
)
},
}
})
let listNum = diyStore.editComponent.list.length
let goodsIdNum = diyStore.editComponent.goods_ids.length
diyStore.editComponent.goods_ids.splice(listNum, goodsIdNum)
let listNum = diyStore.editComponent.list.length;
let goodsIdNum = diyStore.editComponent.goods_ids.length;
diyStore.editComponent.goods_ids.splice(listNum, goodsIdNum);
})
})
@ -250,32 +153,33 @@ const addItem = () => {
id: diyStore.generateRandom(),
title: {
text: '标题',
textColor: '#303133',
textColor: '#303133'
},
moreTitle: {
text: '精选',
startColor: '#FF7234',
endColor: '#FF213F',
endColor: '#FF213F'
},
listFrame: {
startColor: '#FFE5E5',
endColor: '#FFF5F0',
endColor: '#FFF5F0'
},
button : {
text: '首单',
textColor: '#FFFFFF',
color: '#FF1128',
text: "首单",
textColor: "#FFFFFF",
color: "#FF1128",
},
goodsId: [],
})
}
const deleteTempFn = (index) =>{
diyStore.editComponent.list.splice(index, 1)
diyStore.editComponent.goods_ids.splice(index, 1)
diyStore.editComponent.list.splice(index,1);
diyStore.editComponent.goods_ids.splice(index,1);
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

1
admin/src/addon/shop/views/diy/components/edit-shop-member-info.vue

@ -46,6 +46,7 @@ const diyStore = useDiyStore()
diyStore.editComponent.ignore = ['componentBgUrl'] //
defineExpose({})
</script>
<style lang="scss" scoped></style>

391
admin/src/addon/shop/views/diy/components/edit-shop-newcomer.vue

@ -5,43 +5,28 @@
<h3 class="mb-[10px]">{{ t('styleRecommend') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('selectStyle')" class="flex">
<span
class="text-primary flex-1 cursor-pointer"
@click="showTitleStyle"
>{{ diyStore.editComponent.style.title }}</span
>
<span class="text-primary flex-1 cursor-pointer" @click="showTitleStyle">{{ diyStore.editComponent.style.title }}</span>
<el-icon>
<ArrowRight />
</el-icon>
</el-form-item>
</el-form>
<el-dialog
v-model="showTitleDialog"
:title="t('selectStyle')"
width="460px"
>
<el-dialog v-model="showTitleDialog" :title="t('selectStyle')" width="460px">
<div class="flex flex-wrap">
<template v-for="(item,index) in styleList" :key="index">
<div
:class="{ 'border-primary': selectStyle.value == item.value }"
@click="changeTitleStyle(item)"
class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]"
>
<div :class="{ 'border-primary': selectStyle.value == item.value }" @click="changeTitleStyle(item)" class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
<img :src="img(item.url)" />
</div>
</template>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showTitleDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="confirmTitleStyle">{{
t('confirm')
}}</el-button>
<el-button @click="showTitleDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmTitleStyle">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
@ -51,78 +36,32 @@
<el-form-item :label="t('image')">
<upload-image v-model="diyStore.editComponent.textImg" :limit="1"/>
</el-form-item>
<el-form-item
:label="t('subTitle')"
v-show="
diyStore.editComponent &&
diyStore.editComponent.style &&
diyStore.editComponent.style.value == 'style-3'
"
>
<el-input
v-model.trim="diyStore.editComponent.subTitle.text"
:placeholder="t('subTitlePlaceholder')"
clearable
maxlength="8"
show-word-limit
/>
<el-form-item :label="t('subTitle')" v-show="diyStore.editComponent && diyStore.editComponent.style && diyStore.editComponent.style.value == 'style-3'">
<el-input v-model.trim="diyStore.editComponent.subTitle.text" :placeholder="t('subTitlePlaceholder')" clearable maxlength="8" show-word-limit />
</el-form-item>
<el-form-item
:label="t('link')"
v-show="
diyStore.editComponent &&
diyStore.editComponent.style &&
diyStore.editComponent.style.value == 'style-3'
"
>
<el-form-item :label="t('link')" v-show="diyStore.editComponent && diyStore.editComponent.style && diyStore.editComponent.style.value == 'style-3'">
<diy-link v-model="diyStore.editComponent.subTitle.link"/>
</el-form-item>
</el-form>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectSource') }}</h3>
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group
v-model="diyStore.editComponent.source"
:title="t('goodsSelectPopupSelectGoodsButton')"
>
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('goodsSelectPopupAllGoods') }}</el-radio>
<el-radio label="custom">{{
t('manualSelectionSources')
}}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('goodsNum')"
v-if="
diyStore.editComponent.source == 'all' ||
diyStore.editComponent.source == 'category'
"
>
<el-slider
class="goods-list-slider"
show-input
v-model="diyStore.editComponent.num"
:min="1"
max="20"
size="small"
/>
<el-form-item :label="t('goodsNum')" v-if="diyStore.editComponent.source == 'all' || diyStore.editComponent.source == 'category'">
<el-slider class="goods-list-slider" show-input v-model="diyStore.editComponent.num" :min="1" max="20" size="small" />
</el-form-item>
<el-form-item
:label="t('customGoods')"
v-if="diyStore.editComponent.source == 'custom'"
>
<newcomer-goods-select-popup
ref="goodsSelectPopupRef"
v-model="diyStore.editComponent.goods_ids"
:min="1"
:max="99"
mode="sku"
/>
<el-form-item :label="t('customGoods')" v-if="diyStore.editComponent.source == 'custom'">
<newcomer-goods-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.goods_ids" :min="1" :max="99" mode="sku" />
</el-form-item>
</el-form>
</div>
</div>
@ -132,58 +71,23 @@
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('topRounded')">
<el-slider
v-model="diyStore.editComponent.topElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider
v-model="diyStore.editComponent.bottomElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
<div
class="edit-attr-item-wrap"
v-if="
diyStore.editComponent &&
diyStore.editComponent.style &&
diyStore.editComponent.style.value == 'style-3'
"
>
<div class="edit-attr-item-wrap" v-if="diyStore.editComponent && diyStore.editComponent.style && diyStore.editComponent.style.value == 'style-3'">
<h3 class="mb-[10px]">{{ t('subTitleStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('textColor')">
<el-color-picker
v-model="diyStore.editComponent.subTitle.textColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.subTitle.textColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('subTextBgColor')">
<el-color-picker
v-model="diyStore.editComponent.subTitle.startColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<icon
name="iconfont iconmap-connect"
size="20px"
class="block !text-gray-400 mx-[5px]"
/>
<el-color-picker
v-model="diyStore.editComponent.subTitle.endColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.subTitle.startColor" show-alpha :predefine="diyStore.predefineColors"/>
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="diyStore.editComponent.subTitle.endColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
@ -191,35 +95,15 @@
<h3 class="mb-[10px]">{{ t('countDownStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('newcomerNumberColor')">
<el-color-picker
v-model="diyStore.editComponent.countDown.numberColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.countDown.numberColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('newcomerNumberBg')">
<el-color-picker
v-model="diyStore.editComponent.countDown.numberBg.startColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<icon
name="iconfont iconmap-connect"
size="20px"
class="block !text-gray-400 mx-[5px]"
/>
<el-color-picker
v-model="diyStore.editComponent.countDown.numberBg.endColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.countDown.numberBg.startColor" show-alpha :predefine="diyStore.predefineColors"/>
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]"/>
<el-color-picker v-model="diyStore.editComponent.countDown.numberBg.endColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
<el-form-item :label="t('newcomerOtherColor')">
<el-color-picker
v-model="diyStore.editComponent.countDown.otherColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.countDown.otherColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
@ -257,20 +141,20 @@ diyStore.editComponent.verify = (index: number) => {
const showTitleDialog = ref(false)
const showTitleStyle = () => {
selectStyle.title = diyStore.editComponent.style.title
selectStyle.value = diyStore.editComponent.style.value
selectStyle.title = diyStore.editComponent.style.title;
selectStyle.value = diyStore.editComponent.style.value;
showTitleDialog.value = true
}
const changeTitleStyle = (item:any) => {
selectStyle.title = item.title
selectStyle.value = item.value
selectStyle.title = item.title;
selectStyle.value = item.value;
}
const confirmTitleStyle = () => {
diyStore.editComponent.style.title = selectStyle.title
diyStore.editComponent.style.value = selectStyle.value
initStyle(diyStore.editComponent.style.value)
diyStore.editComponent.style.title = selectStyle.title;
diyStore.editComponent.style.value = selectStyle.value;
initStyle(diyStore.editComponent.style.value);
showTitleDialog.value = false
}
@ -278,134 +162,125 @@ const styleList = reactive([
{
url : 'addon/shop/diy/newcomer/style_01.png',
title:'风格1',
value: 'style-1',
},
{
value:'style-1'
},{
url : 'addon/shop/diy/newcomer/style_02.png',
title:'风格2',
value: 'style-2',
},
{
value:'style-2'
},{
url : 'addon/shop/diy/newcomer/style_03.png',
title:'风格3',
value: 'style-3',
},
{
value:'style-3'
},{
url : 'addon/shop/diy/newcomer/style_04.png',
title:'风格4',
value: 'style-4',
},
value:'style-4'
}
])
const initStyle = (style: any) => {
if (style == 'style-1') {
diyStore.editComponent.textImg =
'addon/shop/diy/newcomer/style_title_01.png'
diyStore.editComponent.countDown.numberColor = 'rgba(255, 0, 0, 1)'
diyStore.editComponent.countDown.numberBg.startColor =
'rgba(255, 255, 255, 1)'
diyStore.editComponent.countDown.numberBg.endColor = ''
diyStore.editComponent.countDown.otherColor = 'rgba(255, 255, 255, 1)'
diyStore.editComponent.textColor = '#303133'
diyStore.editComponent.pageStartBgColor = ''
diyStore.editComponent.pageEndBgColor = ''
diyStore.editComponent.pageGradientAngle = 'to bottom'
diyStore.editComponent.componentStartBgColor = '#ff6D1A'
diyStore.editComponent.componentEndBgColor = 'rgba(255, 70, 56, 1)'
diyStore.editComponent.componentGradientAngle = 'to right'
diyStore.editComponent.bottomRounded = 12
diyStore.editComponent.topRounded = 12
diyStore.editComponent.elementBgColor = ''
diyStore.editComponent.bottomElementRounded = 10
diyStore.editComponent.topElementRounded = 10
diyStore.editComponent.margin.top = 10
diyStore.editComponent.margin.bottom = 0
diyStore.editComponent.margin.both = 10
diyStore.editComponent.textImg = "addon/shop/diy/newcomer/style_title_01.png";
diyStore.editComponent.countDown.numberColor = "rgba(255, 0, 0, 1)";
diyStore.editComponent.countDown.numberBg.startColor = "rgba(255, 255, 255, 1)";
diyStore.editComponent.countDown.numberBg.endColor = "";
diyStore.editComponent.countDown.otherColor = "rgba(255, 255, 255, 1)";
diyStore.editComponent.textColor = "#303133";
diyStore.editComponent.pageStartBgColor = "";
diyStore.editComponent.pageEndBgColor = "";
diyStore.editComponent.pageGradientAngle = "to bottom";
diyStore.editComponent.componentStartBgColor = "#ff6D1A";
diyStore.editComponent.componentEndBgColor = "rgba(255, 70, 56, 1)";
diyStore.editComponent.componentGradientAngle = "to right";
diyStore.editComponent.bottomRounded = 12;
diyStore.editComponent.topRounded = 12;
diyStore.editComponent.elementBgColor = "";
diyStore.editComponent.bottomElementRounded = 10;
diyStore.editComponent.topElementRounded = 10;
diyStore.editComponent.margin.top = 10;
diyStore.editComponent.margin.bottom = 0;
diyStore.editComponent.margin.both = 10;
} else if (style == 'style-2') {
diyStore.editComponent.textImg =
'addon/shop/diy/newcomer/style_title_02.png'
diyStore.editComponent.countDown.numberColor = 'rgba(255, 255, 255, 1)'
diyStore.editComponent.countDown.numberBg.startColor =
'rgba(255, 44, 54, 1)'
diyStore.editComponent.countDown.numberBg.endColor = ''
diyStore.editComponent.countDown.otherColor = 'rgba(102, 102, 102, 1)'
diyStore.editComponent.textColor = '#303133'
diyStore.editComponent.pageStartBgColor = ''
diyStore.editComponent.pageEndBgColor = ''
diyStore.editComponent.pageGradientAngle = 'to bottom'
diyStore.editComponent.componentStartBgColor = 'rgba(255, 255, 255, 1)'
diyStore.editComponent.componentEndBgColor = ''
diyStore.editComponent.componentGradientAngle = 'to bottom'
diyStore.editComponent.bottomRounded = 12
diyStore.editComponent.topRounded = 12
diyStore.editComponent.elementBgColor = ''
diyStore.editComponent.bottomElementRounded = 5
diyStore.editComponent.topElementRounded = 5
diyStore.editComponent.margin.top = 10
diyStore.editComponent.margin.bottom = 0
diyStore.editComponent.margin.both = 10
diyStore.editComponent.textImg = "addon/shop/diy/newcomer/style_title_02.png";
diyStore.editComponent.countDown.numberColor = "rgba(255, 255, 255, 1)";
diyStore.editComponent.countDown.numberBg.startColor = "rgba(255, 44, 54, 1)";
diyStore.editComponent.countDown.numberBg.endColor = "";
diyStore.editComponent.countDown.otherColor = "rgba(102, 102, 102, 1)";
diyStore.editComponent.textColor = "#303133";
diyStore.editComponent.pageStartBgColor = "";
diyStore.editComponent.pageEndBgColor = "";
diyStore.editComponent.pageGradientAngle = "to bottom";
diyStore.editComponent.componentStartBgColor = "rgba(255, 255, 255, 1)";
diyStore.editComponent.componentEndBgColor = "";
diyStore.editComponent.componentGradientAngle = "to bottom";
diyStore.editComponent.bottomRounded = 12;
diyStore.editComponent.topRounded = 12;
diyStore.editComponent.elementBgColor = "";
diyStore.editComponent.bottomElementRounded = 5;
diyStore.editComponent.topElementRounded = 5;
diyStore.editComponent.margin.top = 10;
diyStore.editComponent.margin.bottom = 0;
diyStore.editComponent.margin.both = 10;
} else if (style == 'style-3') {
diyStore.editComponent.textImg =
'addon/shop/diy/newcomer/style_title_03.png'
diyStore.editComponent.subTitle.text = '查看更多'
diyStore.editComponent.subTitle.textColor = 'rgba(239, 0, 12, 1)'
diyStore.editComponent.subTitle.startColor = 'rgba(255, 248, 217, 1)'
diyStore.editComponent.subTitle.endColor = 'rgba(255, 254, 251, 1)'
diyStore.editComponent.subTitle.link.name = ''
diyStore.editComponent.countDown.numberColor = 'rgba(239, 0, 12, 1)'
diyStore.editComponent.countDown.numberBg.startColor =
'rgba(255, 248, 217, 1)'
diyStore.editComponent.countDown.numberBg.endColor =
'rgba(255, 253, 246, 1)'
diyStore.editComponent.countDown.otherColor = 'rgba(255, 253, 246, 1)'
diyStore.editComponent.textColor = '#303133'
diyStore.editComponent.pageStartBgColor = ''
diyStore.editComponent.pageEndBgColor = ''
diyStore.editComponent.pageGradientAngle = 'to bottom'
diyStore.editComponent.componentStartBgColor = 'rgba(255, 12, 16, 1)'
diyStore.editComponent.componentEndBgColor = 'rgba(255, 101, 18, 1)'
diyStore.editComponent.componentGradientAngle = 'to right'
diyStore.editComponent.bottomRounded = 12
diyStore.editComponent.topRounded = 12
diyStore.editComponent.elementBgColor = ''
diyStore.editComponent.bottomElementRounded = 0
diyStore.editComponent.topElementRounded = 0
diyStore.editComponent.margin.top = 10
diyStore.editComponent.margin.bottom = 0
diyStore.editComponent.margin.both = 10
diyStore.editComponent.textImg = "addon/shop/diy/newcomer/style_title_03.png";
diyStore.editComponent.subTitle.text = "查看更多";
diyStore.editComponent.subTitle.textColor = "rgba(239, 0, 12, 1)";
diyStore.editComponent.subTitle.startColor = "rgba(255, 248, 217, 1)";
diyStore.editComponent.subTitle.endColor = "rgba(255, 254, 251, 1)";
diyStore.editComponent.subTitle.link.name = "";
diyStore.editComponent.countDown.numberColor = "rgba(239, 0, 12, 1)";
diyStore.editComponent.countDown.numberBg.startColor = "rgba(255, 248, 217, 1)";
diyStore.editComponent.countDown.numberBg.endColor = "rgba(255, 253, 246, 1)";
diyStore.editComponent.countDown.otherColor = "rgba(255, 253, 246, 1)";
diyStore.editComponent.textColor = "#303133";
diyStore.editComponent.pageStartBgColor = "";
diyStore.editComponent.pageEndBgColor = "";
diyStore.editComponent.pageGradientAngle = "to bottom";
diyStore.editComponent.componentStartBgColor = "rgba(255, 12, 16, 1)";
diyStore.editComponent.componentEndBgColor = "rgba(255, 101, 18, 1)";
diyStore.editComponent.componentGradientAngle = "to right";
diyStore.editComponent.bottomRounded = 12;
diyStore.editComponent.topRounded = 12;
diyStore.editComponent.elementBgColor = "";
diyStore.editComponent.bottomElementRounded = 0;
diyStore.editComponent.topElementRounded = 0;
diyStore.editComponent.margin.top = 10;
diyStore.editComponent.margin.bottom = 0;
diyStore.editComponent.margin.both = 10;
} else if (style == 'style-4') {
diyStore.editComponent.textImg =
'addon/shop/diy/newcomer/style_title_02.png'
diyStore.editComponent.countDown.numberColor = 'rgba(255, 255, 255, 1)'
diyStore.editComponent.countDown.numberBg.startColor = ''
diyStore.editComponent.countDown.numberBg.endColor = ''
diyStore.editComponent.countDown.otherColor = 'rgba(255, 253, 253, 1)'
diyStore.editComponent.textColor = '#303133'
diyStore.editComponent.pageStartBgColor = ''
diyStore.editComponent.pageEndBgColor = ''
diyStore.editComponent.pageGradientAngle = 'to bottom'
diyStore.editComponent.componentStartBgColor = 'rgba(255, 255, 255, 1)'
diyStore.editComponent.componentEndBgColor = 'rgba(255, 255, 255, 1)'
diyStore.editComponent.componentGradientAngle = 'to bottom'
diyStore.editComponent.bottomRounded = 12
diyStore.editComponent.topRounded = 12
diyStore.editComponent.elementBgColor = ''
diyStore.editComponent.bottomElementRounded = 10
diyStore.editComponent.topElementRounded = 10
diyStore.editComponent.margin.top = 10
diyStore.editComponent.margin.bottom = 0
diyStore.editComponent.margin.both = 10
diyStore.editComponent.textImg = "addon/shop/diy/newcomer/style_title_02.png";
diyStore.editComponent.countDown.numberColor = "rgba(255, 255, 255, 1)";
diyStore.editComponent.countDown.numberBg.startColor = "";
diyStore.editComponent.countDown.numberBg.endColor = "";
diyStore.editComponent.countDown.otherColor = "rgba(255, 253, 253, 1)";
diyStore.editComponent.textColor = "#303133";
diyStore.editComponent.pageStartBgColor = "";
diyStore.editComponent.pageEndBgColor = "";
diyStore.editComponent.pageGradientAngle = "to bottom";
diyStore.editComponent.componentStartBgColor = "rgba(255, 255, 255, 1)";
diyStore.editComponent.componentEndBgColor = "rgba(255, 255, 255, 1)";
diyStore.editComponent.componentGradientAngle = "to bottom";
diyStore.editComponent.bottomRounded = 12;
diyStore.editComponent.topRounded = 12;
diyStore.editComponent.elementBgColor = "";
diyStore.editComponent.bottomElementRounded = 10;
diyStore.editComponent.topElementRounded = 10;
diyStore.editComponent.margin.top = 10;
diyStore.editComponent.margin.bottom = 0;
diyStore.editComponent.margin.both = 10;
}
}
const selectStyle = reactive({
title: diyStore.editComponent.style.title,
value: diyStore.editComponent.style.value,
value: diyStore.editComponent.style.value
})
initStyle(diyStore.editComponent.style.value)
initStyle(diyStore.editComponent.style.value);
defineExpose({})
</script>
<style lang="scss" scoped></style>

45
admin/src/addon/shop/views/diy/components/edit-shop-order-info.vue

@ -5,13 +5,7 @@
<h3 class="mb-[10px]">{{ t('titleContent') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('title')">
<el-input
v-model.trim="diyStore.editComponent.text"
:placeholder="t('titlePlaceholder')"
clearable
maxlength="6"
show-word-limit
/>
<el-input v-model.trim="diyStore.editComponent.text" :placeholder="t('titlePlaceholder')" clearable maxlength="6" show-word-limit />
</el-form-item>
</el-form>
</div>
@ -20,32 +14,21 @@
<h3 class="mb-[10px]">{{ t('subTitle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('more')">
<el-input
v-model.trim="diyStore.editComponent.more.text"
:placeholder="t('morePlaceholder')"
clearable
maxlength="8"
show-word-limit
/>
<el-input v-model.trim="diyStore.editComponent.more.text" :placeholder="t('morePlaceholder')" clearable maxlength="8" show-word-limit />
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
<div class="style-wrap" v-show="diyStore.editTab == 'style'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('titleStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('textFontSize')">
<el-slider
v-model="diyStore.editComponent.fontSize"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:min="12"
:max="30"
/>
<el-slider v-model="diyStore.editComponent.fontSize" show-input size="small" class="ml-[10px] diy-nav-slider" :min="12" :max="30" />
</el-form-item>
<el-form-item :label="t('textFontWeight')">
<el-radio-group v-model="diyStore.editComponent.fontWeight">
@ -72,14 +55,7 @@
<h3 class="mb-[10px]">{{ t('textSet') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('textFontSize')">
<el-slider
v-model="diyStore.editComponent.item.fontSize"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:min="12"
:max="16"
/>
<el-slider v-model="diyStore.editComponent.item.fontSize" show-input size="small" class="ml-[10px] diy-nav-slider" :min="12" :max="16"/>
</el-form-item>
<el-form-item :label="t('textFontWeight')">
<el-radio-group v-model="diyStore.editComponent.item.fontWeight">
@ -88,16 +64,14 @@
</el-radio-group>
</el-form-item>
<el-form-item :label="t('textColor')">
<el-color-picker
v-model="diyStore.editComponent.item.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.item.color" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
@ -135,6 +109,7 @@ const changeStyle = () => {
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

11
admin/src/addon/shop/views/diy/components/edit-shop-search.vue

@ -1,20 +1,16 @@
<template>
<!-- 内容 -->
<div class="content-wrap" v-show="diyStore.editTab == 'content'">
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('goodsSearchSet') }}</h3>
<el-form label-width="100px" class="px-[10px]">
<el-form-item :label="t('goodsSearchText')">
<el-input
v-model.trim="diyStore.editComponent.text"
:placeholder="t('goodsSearchTextPlaceholder')"
clearable
maxlength="20"
show-word-limit
/>
<el-input v-model.trim="diyStore.editComponent.text" :placeholder="t('goodsSearchTextPlaceholder')" clearable maxlength="20" show-word-limit />
</el-form-item>
</el-form>
</div>
</div>
<!-- 样式 -->
@ -22,6 +18,7 @@
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>

231
admin/src/addon/shop/views/diy/components/edit-single-recommend.vue

@ -5,11 +5,7 @@
<h3 class="mb-[10px]">{{ t('titleContent') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('selectStyle')" class="flex">
<span
class="text-primary flex-1 cursor-pointer"
@click="showTitleStyle"
>{{ diyStore.editComponent.titleStyle.title }}</span
>
<span class="text-primary flex-1 cursor-pointer" @click="showTitleStyle">{{ diyStore.editComponent.titleStyle.title }}</span>
<el-icon>
<ArrowRight />
</el-icon>
@ -21,33 +17,18 @@
<diy-link v-model="diyStore.editComponent.textLink"/>
</el-form-item>
<el-form-item :label="t('subTitle')">
<el-input
v-model.trim="diyStore.editComponent.subTitle.text"
:placeholder="t('subTitlePlaceholder')"
clearable
maxlength="8"
show-word-limit
/>
<el-input v-model.trim="diyStore.editComponent.subTitle.text" :placeholder="t('subTitlePlaceholder')" clearable maxlength="8" show-word-limit />
</el-form-item>
<el-form-item :label="t('link')">
<diy-link v-model="diyStore.editComponent.subTitle.link"/>
</el-form-item>
</el-form>
<el-dialog
v-model="showTitleDialog"
:title="t('selectStyle')"
width="460px"
>
<el-dialog v-model="showTitleDialog" :title="t('selectStyle')" width="460px">
<div class="flex flex-wrap">
<template v-for="(item,index) in titleStyleList" :key="index">
<div
:class="{
'border-primary': selectTitleStyle.value == item.value,
}"
@click="changeTitleStyle(item)"
class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]"
>
<div :class="{ 'border-primary': selectTitleStyle.value == item.value }" @click="changeTitleStyle(item)" class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]">
<img :src="img(item.url)" />
</div>
</template>
@ -55,41 +36,25 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="showTitleDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="confirmTitleStyle">{{
t('confirm')
}}</el-button>
<el-button @click="showTitleDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirmTitleStyle">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('selectSource') }}</h3>
<h3 class="mb-[10px]">{{ t("selectSource") }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsSelectPopupSelectGoodsButton')">
<el-radio-group
v-model="diyStore.editComponent.source"
:title="t('goodsSelectPopupSelectGoodsButton')"
>
<el-radio-group v-model="diyStore.editComponent.source" :title="t('goodsSelectPopupSelectGoodsButton')">
<el-radio label="all">{{ t('defaultGoodsSelect') }}</el-radio>
<el-radio label="custom">{{
t('manualSelectionSources')
}}</el-radio>
<el-radio label="custom">{{ t('manualSelectionSources') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('customGoods')"
v-if="diyStore.editComponent.source == 'custom'"
>
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="diyStore.editComponent.goods_ids"
:min="1"
:max="1"
/>
<el-form-item :label="t('customGoods')" v-if="diyStore.editComponent.source == 'custom'">
<goods-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.goods_ids" :min="1" :max="1" />
</el-form-item>
</el-form>
</div>
@ -97,21 +62,14 @@
<div class="edit-attr-item-wrap">
<h3 class="mb-[10px]">{{ t('imageSet') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<div ref="imageBoxRef">
<div
v-for="(item, index) in diyStore.editComponent.list"
:key="item.id"
class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]"
>
<div v-for="(item,index) in diyStore.editComponent.list" :key="item.id" class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
<el-form-item :label="t('image')">
<upload-image v-model="item.imageUrl" :limit="1" />
</el-form-item>
<div
class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
v-show="diyStore.editComponent.list.length > 1"
@click="diyStore.editComponent.list.splice(index, 1)"
>
<div class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]" v-show="diyStore.editComponent.list.length > 1" @click="diyStore.editComponent.list.splice(index,1)">
<icon name="element CircleCloseFilled" color="#bbb" size="20px"/>
</div>
@ -121,34 +79,16 @@
</div>
</div>
<el-button
v-show="diyStore.editComponent.list.length < 10"
class="w-full"
@click="addImageAd"
>{{ t('addImageAd') }}</el-button
>
<el-button v-show="diyStore.editComponent.list.length < 10" class="w-full" @click="addImageAd">{{ t('addImageAd') }}</el-button>
</el-form>
</div>
<el-dialog
v-model="categoryShowDialog"
:title="t('goodsCategoryTitle')"
width="750px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-table
:data="categoryTable.data"
ref="categoryTableRef"
size="large"
v-loading="categoryTable.loading"
height="450px"
@selection-change="handleSelectionChange"
row-key="category_id"
<el-dialog v-model="categoryShowDialog" :title="t('goodsCategoryTitle')" width="750px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-table :data="categoryTable.data" ref="categoryTableRef" size="large" v-loading="categoryTable.loading"
height="450px" @selection-change="handleSelectionChange" row-key="category_id"
:expand-row-keys="expand_category_ids"
:tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }"
>
:tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }">
<template #empty>
<span>{{ !categoryTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -161,17 +101,10 @@
<el-table-column :label="t('categoryImage')" width="170" align="left">
<template #default="{ row }">
<div class="h-[30px]">
<el-image
class="w-[30px] h-[30px]"
:src="img(row.image)"
fit="contain"
>
<el-image class="w-[30px] h-[30px] " :src="img(row.image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[30px] h-[30px]"
src="@/addon/shop/assets/category_default.png"
/>
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
@ -180,12 +113,8 @@
</el-table-column>
</el-table>
<div class="flex items-center justify-end mt-[15px]">
<el-button type="primary" @click="saveCategoryId">{{
t('confirm')
}}</el-button>
<el-button @click="categoryShowDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="saveCategoryId">{{ t('confirm') }}</el-button>
<el-button @click="categoryShowDialog = false">{{ t('cancel') }}</el-button>
</div>
</el-dialog>
</div>
@ -196,11 +125,7 @@
<h3 class="mb-[10px]">{{ t('titleStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('textColor')">
<el-color-picker
v-model="diyStore.editComponent.subTitle.textColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.subTitle.textColor" show-alpha :predefine="diyStore.predefineColors"/>
</el-form-item>
</el-form>
</div>
@ -208,22 +133,10 @@
<h3 class="mb-[10px]">{{ t('carouselStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('topRounded')">
<el-slider
v-model="diyStore.editComponent.topCarouselRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.topCarouselRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider
v-model="diyStore.editComponent.bottomCarouselRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.bottomCarouselRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
@ -231,18 +144,10 @@
<h3 class="mb-[10px]">{{ t('recommendIndicatorStyle') }}</h3>
<el-form label-width="90px" class="px-[10px]">
<el-form-item :label="t('recommendIndicatorColor')">
<el-color-picker
v-model="diyStore.editComponent.indicatorColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.indicatorColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('recommendIndicatorActiveColor')">
<el-color-picker
v-model="diyStore.editComponent.indicatorActiveColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.indicatorActiveColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
</el-form>
</div>
@ -250,57 +155,27 @@
<h3 class="mb-[10px]">{{ t('goodsStyle') }}</h3>
<el-form label-width="80px" class="px-[10px]">
<el-form-item :label="t('goodsBgColor')">
<el-color-picker
v-model="diyStore.editComponent.elementBgColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.elementBgColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsNameColor')">
<el-color-picker
v-model="diyStore.editComponent.goodsNameStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
<div class="mr-[20px]"></div>
<el-radio-group
v-model="diyStore.editComponent.goodsNameStyle.fontWeight"
>
<el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
<el-radio :label="'normal'">{{ t('fontWeightNormal') }}</el-radio>
<el-radio :label="'bold'">{{ t('fontWeightBold') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsPriceColor')">
<el-color-picker
v-model="diyStore.editComponent.priceStyle.mainColor"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.priceStyle.mainColor" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('goodsBtnColor')">
<el-color-picker
v-model="diyStore.editComponent.saleStyle.color"
show-alpha
:predefine="diyStore.predefineColors"
/>
<el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
</el-form-item>
<el-form-item :label="t('topRounded')">
<el-slider
v-model="diyStore.editComponent.topElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
<el-form-item :label="t('bottomRounded')">
<el-slider
v-model="diyStore.editComponent.bottomElementRounded"
show-input
size="small"
class="ml-[10px] diy-nav-slider"
:max="50"
/>
<el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
</el-form-item>
</el-form>
</div>
@ -324,26 +199,26 @@ diyStore.editComponent.ignore = ['componentBgUrl'] // 忽略公共属性
const selectTitleStyle = reactive({
title: diyStore.editComponent.titleStyle.title,
value: diyStore.editComponent.titleStyle.value,
value: diyStore.editComponent.titleStyle.value
})
//
const showTitleDialog = ref(false)
const showTitleStyle = () => {
selectTitleStyle.title = diyStore.editComponent.titleStyle.title
selectTitleStyle.value = diyStore.editComponent.titleStyle.value
selectTitleStyle.title = diyStore.editComponent.titleStyle.title;
selectTitleStyle.value = diyStore.editComponent.titleStyle.value;
showTitleDialog.value = true
}
const changeTitleStyle = (item:any) => {
selectTitleStyle.title = item.title
selectTitleStyle.value = item.value
selectTitleStyle.title = item.title;
selectTitleStyle.value = item.value;
}
const confirmTitleStyle = () => {
diyStore.editComponent.titleStyle.title = selectTitleStyle.title
diyStore.editComponent.titleStyle.value = selectTitleStyle.value
diyStore.editComponent.titleStyle.title = selectTitleStyle.title;
diyStore.editComponent.titleStyle.value = selectTitleStyle.value;
showTitleDialog.value = false
}
@ -351,8 +226,8 @@ const titleStyleList = reactive([
{
url : 'addon/shop/diy/single_recommend/title_style_01.png',
title:'风格1',
value: 'style-1',
},
value:'style-1'
}
])
const addImageAd = () => {
@ -361,7 +236,7 @@ const addImageAd = () => {
imageUrl: '',
imgWidth: 0,
imgHeight: 0,
link: { name: '' },
link: { name: '' }
})
}
@ -381,7 +256,7 @@ diyStore.editComponent.verify = (index: number) => {
res.message = t('imageUrlTip')
return res
}
})
});
return res
}
@ -404,12 +279,10 @@ let currCategoryData: any = null
const loadCategoryList = () => {
categoryTable.loading = true
getCategoryTree()
.then((res) => {
getCategoryTree().then(res => {
categoryTable.loading = false
categoryTable.data = res.data
})
.catch(() => {
}).catch(() => {
categoryTable.loading = false
})
}
@ -425,7 +298,7 @@ const handleSelectionChange = (val: string | any[]) => {
const saveCategoryId = () => {
diyStore.editComponent.goods_category = currCategoryData.category_id
diyStore.editComponent.goods_category_name = currCategoryData.category_name
diyStore.editComponent.goods_category_name = currCategoryData.category_name;
categoryShowDialog.value = false
}

204
admin/src/addon/shop/views/goods/attr.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-lg">{{pageName}}</span>
<el-button type="primary" @click="addEvent">
@ -8,150 +9,70 @@
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="goodsAttrTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="goodsAttrTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('attrName')" prop="attr_name">
<el-input
v-model.trim="goodsAttrTable.searchParam.attr_name"
:placeholder="t('attrNamePlaceholder')"
/>
<el-input v-model.trim="goodsAttrTable.searchParam.attr_name" :placeholder="t('attrNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadShopGoodsAttrList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadShopGoodsAttrList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="goodsAttrTable.data"
size="large"
v-loading="goodsAttrTable.loading"
@sort-change="sortChange"
>
<el-table :data="goodsAttrTable.data" size="large" v-loading="goodsAttrTable.loading" @sort-change="sortChange">
<template #empty>
<span>{{ !goodsAttrTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="attr_name"
:label="t('attrName')"
min-width="320"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="sort"
:label="t('sort')"
min-width="120"
sortable="custom"
>
<el-table-column prop="attr_name" :label="t('attrName')" min-width="320" :show-overflow-tooltip="true"/>
<el-table-column prop="sort" :label="t('sort')" min-width="120" sortable="custom">
<template #default="{ row }">
<el-input
v-model.trim="row.sort"
class="!w-[100px]"
maxlength="8"
@blur="sortInputListener(row.sort, row)"
/>
<el-input v-model.trim="row.sort" class="!w-[100px]" maxlength="8" @blur="sortInputListener(row.sort, row)" />
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="manageEvent(row)">{{
t('manage')
}}</el-button>
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.attr_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="manageEvent(row)">{{ t('manage') }}</el-button>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.attr_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="goodsAttrTable.page"
v-model:page-size="goodsAttrTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="goodsAttrTable.total"
@size-change="loadShopGoodsAttrList()"
@current-change="loadShopGoodsAttrList"
/>
<el-pagination v-model:current-page="goodsAttrTable.page" v-model:page-size="goodsAttrTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="goodsAttrTable.total"
@size-change="loadShopGoodsAttrList()" @current-change="loadShopGoodsAttrList" />
</div>
</div>
</el-card>
<el-dialog
v-model="showDialog"
:title="titleDialog"
width="500px"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-dialog v-model="showDialog" :title="titleDialog" width="500px" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('attrName')" prop="attr_name">
<el-input
v-model.trim="formData.attr_name"
clearable
:placeholder="t('attrNamePlaceholder')"
class="input-width"
maxlength="20"
/>
<el-input v-model.trim="formData.attr_name" clearable :placeholder="t('attrNamePlaceholder')" class="input-width" maxlength="20" />
</el-form-item>
<el-form-item :label="t('sort')" >
<el-input
v-model.trim="formData.sort"
maxlength="8"
show-word-limit
clearable
:placeholder="t('sortPlaceholder')"
class="input-width"
@keyup="filterNumber($event)"
/>
<el-input v-model.trim="formData.sort" maxlength="8" show-word-limit clearable :placeholder="t('sortPlaceholder')" class="input-width" @keyup="filterNumber($event)"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
@ -161,17 +82,11 @@ import { t } from '@/lang'
import { ElMessage,ElMessageBox,FormInstance } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { debounce,filterNumber } from '@/utils/common'
import {
getAttrPageList,
addAttr,
deleteAttr,
modifyAttrSort,
editAttr,
} from '@/addon/shop/api/goods'
import { getAttrPageList, addAttr, deleteAttr,modifyAttrSort,editAttr} from '@/addon/shop/api/goods'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const pageName = route.meta.title;
const goodsAttrTable = reactive({
page: 1,
@ -182,8 +97,8 @@ const goodsAttrTable = reactive({
searchParam: {
attr_name: '',
order: '',
sort: '',
},
sort: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -195,7 +110,7 @@ const titleDialog = ref('')
const formData = reactive({
attr_id: 0,
attr_name: '',
sort: 0,
sort: 0
})
const formRef = ref<FormInstance>()
@ -204,8 +119,8 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
attr_name: [
{ required: true, message: t('attrNamePlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('attrNamePlaceholder'), trigger: 'blur' }
]
}
})
@ -234,14 +149,12 @@ const loadShopGoodsAttrList = (page: number = 1) => {
getAttrPageList({
page: goodsAttrTable.page,
limit: goodsAttrTable.limit,
...goodsAttrTable.searchParam,
})
.then((res) => {
...goodsAttrTable.searchParam
}).then(res => {
goodsAttrTable.loading = false
goodsAttrTable.data = res.data.data
goodsAttrTable.total = res.data.total
})
.catch(() => {
}).catch(() => {
goodsAttrTable.loading = false
})
}
@ -252,19 +165,19 @@ loadShopGoodsAttrList()
* 添加商品参数
*/
const addEvent = () => {
formData.attr_id = 0
formData.attr_name = ''
formData.sort = 0
titleDialog.value = t('addShopGoodsAttr')
formData.attr_id = 0;
formData.attr_name = '';
formData.sort = 0;
titleDialog.value = t('addShopGoodsAttr');
showDialog.value = true
}
//
const editEvent = (data:any)=>{
formData.attr_id = data.attr_id
formData.attr_name = data.attr_name
formData.sort = data.sort
titleDialog.value = t('updateShopGoodsAttr')
formData.attr_id = data.attr_id;
formData.attr_name = data.attr_name;
formData.sort = data.sort;
titleDialog.value = t('updateShopGoodsAttr');
showDialog.value = true
}
@ -279,13 +192,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
save(formData)
.then((res) => {
save(formData).then(res => {
loading.value = false
showDialog.value = false
loadShopGoodsAttrList()
})
.catch((err) => {
}).catch(err => {
loading.value = false
})
}
@ -304,16 +215,17 @@ const manageEvent = (data: any) => {
* 删除商品参数
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('goodsAttrDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('goodsAttrDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteAttr(id)
.then(() => {
}
).then(() => {
deleteAttr(id).then(() => {
loadShopGoodsAttrList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -322,7 +234,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !/^\d{0,8}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${t('sortTips')}`,
message: `${ t('sortTips') }`
})
return
}
@ -331,8 +243,9 @@ const sortInputListener = debounce((sort, row) => {
}
modifyAttrSort({
attr_id: row.attr_id,
sort,
}).then((res) => {})
sort
}).then((res) => {
})
})
const resetForm = (formEl: FormInstance | undefined) => {
@ -342,4 +255,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

386
admin/src/addon/shop/views/goods/attr_edit.vue

@ -1,20 +1,13 @@
<template>
<div class="main-container">
<el-card class="card !border-none mb-[15px]" shadow="never">
<el-page-header
:content="pageName"
:icon="ArrowLeft"
@back="router.push(`/shop/goods/attr`)"
/>
<el-page-header :content="pageName" :icon="ArrowLeft" @back="router.push(`/shop/goods/attr`)" />
</el-card>
<el-card class="box-card !border-none" shadow="never">
<!-- 商品参数基础信息 -->
<el-row
:gutter="20"
class="text-[14px]"
v-if="Object.keys(attrInfo).length"
>
<el-row :gutter="20" class="text-[14px]" v-if="Object.keys(attrInfo).length">
<el-col :span="8">
<label>{{ t('attrName') }}</label>
<span class="ml-[10px]">{{ attrInfo.attr_name }}</span>
@ -24,38 +17,20 @@
<span class="ml-[10px]">{{ attrInfo.sort }}</span>
</el-col>
<el-col :span="6">
<el-button type="primary" link @click="editEvent">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="editEvent">{{ t('edit') }}</el-button>
</el-col>
</el-row>
<el-button type="primary" @click="openAttrValueEvent" class="my-[15px]">{{
t('addShopGoodsAttr')
}}</el-button>
<el-button type="primary" @click="openAttrValueEvent" class="my-[15px]">{{ t('addShopGoodsAttr') }}</el-button>
<el-table
:data="goodsAttrTable.data"
size="large"
v-loading="goodsAttrTable.loading"
>
<el-table :data="goodsAttrTable.data" size="large" v-loading="goodsAttrTable.loading">
<template #empty>
<span>{{ !goodsAttrTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="attr_value_name"
:label="t('attrValueName')"
min-width="200"
:show-overflow-tooltip="true"
/>
<el-table-column
prop="type"
:label="t('attrValueType')"
min-width="100"
:show-overflow-tooltip="true"
>
<el-table-column prop="attr_value_name" :label="t('attrValueName')" min-width="200" :show-overflow-tooltip="true"/>
<el-table-column prop="type" :label="t('attrValueType')" min-width="100" :show-overflow-tooltip="true">
<template #default="{ row }">
<template v-if="row.type == 'radio'">
<span>{{ t('attrValueTypeRadio') }}</span>
@ -69,18 +44,11 @@
</template>
</el-table-column>
<el-table-column
prop="child"
:label="t('attrValueChild')"
min-width="320"
:show-overflow-tooltip="true"
>
<el-table-column prop="child" :label="t('attrValueChild')" min-width="320" :show-overflow-tooltip="true">
<template #default="{ row }">
<template v-if="row.type != 'text'">
<template v-for="(item,index) in row.child">
<span :class="{ 'mr-[5px]': index + 1 != row.child.length }">{{
item.name
}}</span>
<span :class="{ 'mr-[5px]' : (index+1) != row.child.length }">{{ item.name }}</span>
</template>
</template>
<template v-else>
@ -89,229 +57,100 @@
</template>
</el-table-column>
<el-table-column
prop="sort"
:label="t('sort')"
min-width="120"
sortable="custom"
>
<el-table-column prop="sort" :label="t('sort')" min-width="120" sortable="custom">
<template #default="{ row,$index }">
<el-input
v-model.trim="row.sort"
class="w-[70px]"
maxlength="8"
@input="sortInputListener($event, row)"
/>
<el-input v-model.trim="row.sort" class="w-[70px]" maxlength="8" @input="sortInputListener($event, row)" />
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row,$index }">
<el-button
type="primary"
link
@click="editAttrValueEvent(row, $index)"
>{{ t('edit') }}</el-button
>
<el-button type="primary" link @click="deleteEvent($index)">{{
t('delete')
}}</el-button>
<el-button type="primary" link @click="editAttrValueEvent(row,$index)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent($index)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 编辑商品参数信息 -->
<el-dialog
v-model="showDialogByAttr"
:title="t('updateAttr')"
width="500px"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRulesByAttr"
class="page-form"
v-loading="loadingByAttr"
>
<el-dialog v-model="showDialogByAttr" :title="t('updateAttr')" width="500px" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRulesByAttr" class="page-form" v-loading="loadingByAttr">
<el-form-item :label="t('attrName')" prop="attr_name">
<el-input
v-model.trim="formData.attr_name"
clearable
:placeholder="t('attrNamePlaceholder')"
class="input-width"
maxlength="20"
/>
<el-input v-model.trim="formData.attr_name" clearable :placeholder="t('attrNamePlaceholder')" class="input-width" maxlength="20" />
</el-form-item>
<el-form-item :label="t('sort')" >
<el-input
v-model.trim="formData.sort"
maxlength="8"
show-word-limit
clearable
:placeholder="t('sortPlaceholder')"
class="input-width"
@keyup="filterNumber($event)"
/>
<el-input v-model.trim="formData.sort" maxlength="8" show-word-limit clearable :placeholder="t('sortPlaceholder')" class="input-width" @keyup="filterNumber($event)"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialogByAttr = false">{{
t('cancel')
}}</el-button>
<el-button
type="primary"
:loading="loadingByAttr"
@click="confirmAttr(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button @click="showDialogByAttr = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loadingByAttr" @click="confirmAttr(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
<!-- 编辑商品参数值信息 -->
<el-dialog
v-model="showDialogByAttrValue"
:title="attrValueTitleDialog"
width="700px"
:destroy-on-close="true"
>
<el-form
:model="formAttrValueData"
label-width="120px"
ref="formAttrValueRef"
:rules="formRulesByAttrValue"
class="page-form"
v-loading="loadingByAttrValue"
>
<el-dialog v-model="showDialogByAttrValue" :title="attrValueTitleDialog" width="700px" :destroy-on-close="true">
<el-form :model="formAttrValueData" label-width="120px" ref="formAttrValueRef" :rules="formRulesByAttrValue" class="page-form" v-loading="loadingByAttrValue">
<el-form-item :label="t('attrValueName')" prop="attr_value_name">
<el-input
v-model.trim="formAttrValueData.attr_value_name"
clearable
:placeholder="t('attrValueNamePlaceholder')"
class="input-width"
maxlength="20"
show-word-limit
/>
<el-input v-model.trim="formAttrValueData.attr_value_name" clearable :placeholder="t('attrValueNamePlaceholder')" class="input-width" maxlength="20" show-word-limit />
</el-form-item>
<el-form-item :label="t('attrValueType')">
<el-select
v-model="formAttrValueData.type"
class="!w-[150px]"
:disabled="actionAttrValueIndex > -1"
>
<el-option
v-for="item in attrValueTypeOptions"
:label="item.label"
:value="item.value"
/>
<el-select v-model="formAttrValueData.type" class="!w-[150px]" :disabled="actionAttrValueIndex > -1">
<el-option v-for="item in attrValueTypeOptions" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="t('sort')" >
<el-input
v-model.trim="formAttrValueData.sort"
maxlength="8"
show-word-limit
clearable
:placeholder="t('sortPlaceholder')"
class="!w-[150px]"
@keyup="filterNumber($event)"
/>
<el-input v-model.trim="formAttrValueData.sort" maxlength="8" show-word-limit clearable :placeholder="t('sortPlaceholder')" class="!w-[150px]" @keyup="filterNumber($event)"/>
</el-form-item>
<template v-if="formAttrValueData.type != 'text'">
<el-table :data="formAttrValueData.child" size="large">
<template #empty>
<span>{{
formAttrValueData.child.length == 0 ? t('emptyData') : ''
}}</span>
<span>{{ formAttrValueData.child.length == 0 ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="name"
:label="t('attrValueName')"
min-width="200"
>
<el-table-column prop="name" :label="t('attrValueName')" min-width="200">
<template #default="{ row }">
<el-input
v-model.trim="row.name"
class="input-width"
maxlength="20"
:placeholder="t('attrValueNamePlaceholder')"
clearable
show-word-limit
/>
<el-input v-model.trim="row.name" class="input-width" maxlength="20" :placeholder="t('attrValueNamePlaceholder')" clearable show-word-limit />
</template>
</el-table-column>
<el-table-column prop="name" :label="t('sort')" min-width="120">
<template #default="{ row }">
<el-input
v-model.trim="row.sort"
class="!w-[150px]"
maxlength="8"
:placeholder="t('sortPlaceholder')"
clearable
show-word-limit
@keyup="filterNumber($event)"
/>
<el-input v-model.trim="row.sort" class="!w-[150px]" maxlength="8" :placeholder="t('sortPlaceholder')" clearable show-word-limit @keyup="filterNumber($event)" />
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="60"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="60">
<template #default="{ row,$index }">
<el-button
type="primary"
link
@click="deleteAttrValueEvent(row, $index)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="deleteAttrValueEvent(row,$index)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<el-button
type="primary"
plain
@click="addAttrValueEvent"
class="my-[10px]"
v-show="formAttrValueData.child.length < maxLength"
>{{ t('addAttrValue') }}</el-button
>
<el-button type="primary" plain @click="addAttrValueEvent" class="my-[10px]" v-show="formAttrValueData.child.length < maxLength">{{ t('addAttrValue') }}</el-button>
</template>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialogByAttrValue = false">{{
t('cancel')
}}</el-button>
<el-button
type="primary"
:loading="loadingByAttrValue"
@click="confirmAttrValue(formAttrValueRef)"
>{{ t('confirm') }}</el-button
>
<el-button @click="showDialogByAttrValue = false">{{ t('cancel') }}</el-button>
<el-button type="primary" :loading="loadingByAttrValue" @click="confirmAttrValue(formAttrValueRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
@ -327,7 +166,7 @@ import { cloneDeep } from 'lodash-es'
const route = useRoute()
const router = useRouter()
const pageName = route.meta.title
const pageName = route.meta.title;
route.query.attr_id = route.query.attr_id || 0
@ -338,7 +177,7 @@ const maxLength = ref(30) // 参数值最大数量控制
const goodsAttrTable = reactive({
loading: true,
data: [],
data: []
})
const loadAttrInfo = ()=> {
@ -346,7 +185,7 @@ const loadAttrInfo = () => {
getAttrInfo(attrId.value).then((res: any) => {
Object.assign(attrInfo, res.data)
if (attrInfo.attr_value_format) {
attrInfo.attr_value_format = JSON.parse(attrInfo.attr_value_format)
attrInfo.attr_value_format = JSON.parse(attrInfo.attr_value_format);
} else {
attrInfo.attr_value_format = []
}
@ -354,7 +193,7 @@ const loadAttrInfo = () => {
goodsAttrTable.data = cloneDeep(attrInfo.attr_value_format)
goodsAttrTable.data.sort((a: any, b: any) => {
return b.sort - a.sort
})
});
goodsAttrTable.loading = false
})
@ -368,23 +207,23 @@ const formRef = ref<FormInstance>()
const formData = reactive({
attr_id: 0,
attr_name: '',
sort: 0,
sort: 0
})
//
const formRulesByAttr = computed(() => {
return {
attr_name: [
{ required: true, message: t('attrNamePlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('attrNamePlaceholder'), trigger: 'blur' }
]
}
})
//
const editEvent = (data:any)=>{
formData.attr_id = attrInfo.attr_id
formData.attr_name = attrInfo.attr_name
formData.sort = attrInfo.sort
formData.attr_id = attrInfo.attr_id;
formData.attr_name = attrInfo.attr_name;
formData.sort = attrInfo.sort;
showDialogByAttr.value = true
}
@ -398,13 +237,11 @@ const confirmAttr = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => {
if (valid) {
loadingByAttr.value = true
editAttr(formData)
.then((res) => {
editAttr(formData).then(res => {
loadingByAttr.value = false
showDialogByAttr.value = false
loadAttrInfo()
})
.catch((err) => {
}).catch(err => {
loadingByAttr.value = false
})
}
@ -421,17 +258,15 @@ const deleteEvent = (index: any) => {
if (repeatAttrValue.value) return
repeatAttrValue.value = true
attrInfo.attr_value_format.splice(index, 1)
attrInfo.attr_value_format.splice(index, 1);
let data = {
attr_id: attrId.value,
attr_value_format: JSON.stringify(attrInfo.attr_value_format),
}
modifyAttrValue(data)
.then((res) => {
attr_value_format: JSON.stringify(attrInfo.attr_value_format)
};
modifyAttrValue(data).then(res => {
repeatAttrValue.value = false
loadAttrInfo()
})
.catch((err) => {
}).catch(err => {
repeatAttrValue.value = false
})
})
@ -446,44 +281,40 @@ const formAttrValueData: any = reactive({
attr_value_name: '',
type: 'radio',
sort: 0,
child: <any>[],
child: <any>[]
})
//
const attrValueTypeOptions = reactive([
{
label:'单选',
value: 'radio',
value:'radio'
},
{
label:'多选',
value: 'checkbox',
value:'checkbox'
},
{
label:'输入',
value: 'text',
},
value:'text'
}
])
// 便
const generateRandom = () => {
return (
Math.floor(new Date().getSeconds()) +
Math.floor(new Date().getMilliseconds())
)
return (Math.floor(new Date().getSeconds()) + Math.floor(new Date().getMilliseconds()));
}
//
const openAttrValueEvent = ()=> {
formAttrValueData.attr_value_id =
attrInfo.attr_value_format.length + generateRandom()
formAttrValueData.attr_value_name = ''
formAttrValueData.type = 'radio'
formAttrValueData.sort = 0
formAttrValueData.child = []
showDialogByAttrValue.value = true
attrValueTitleDialog.value = t('addShopGoodsAttr')
formAttrValueData.attr_value_id = attrInfo.attr_value_format.length + generateRandom();
formAttrValueData.attr_value_name = '';
formAttrValueData.type = 'radio';
formAttrValueData.sort = 0;
formAttrValueData.child = [];
showDialogByAttrValue.value = true;
attrValueTitleDialog.value = t('addShopGoodsAttr');
actionAttrValueId.value = -1
}
@ -492,27 +323,26 @@ const sortInputListener = debounce((sort: any, row: any) => {
if (isNaN(sort) || !/^\d{0,10}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${t('sortTips')}`,
message: `${t('sortTips')}`
})
return
}
for (let i = 0; i < attrInfo.attr_value_format.length; i++) {
if (attrInfo.attr_value_format[i].attr_value_id == row.attr_value_id) {
attrInfo.attr_value_format[i].sort = sort
break
attrInfo.attr_value_format[i].sort = sort;
break;
}
}
let data = {
attr_id: attrId.value,
attr_value_format: JSON.stringify(attrInfo.attr_value_format),
}
modifyAttrValue(data)
.then((res) => {
attr_value_format: JSON.stringify(attrInfo.attr_value_format)
};
modifyAttrValue(data).then(res => {
loadAttrInfo()
}).catch(err => {
})
.catch((err) => {})
})
//
@ -520,28 +350,24 @@ const addAttrValueEvent = () => {
formAttrValueData.child.push({
id: formAttrValueData.child.length + generateRandom(),
name: '',
sort: 0,
})
sort: 0
});
}
//
const editAttrValueEvent = (data:any,index:any)=> {
attrValueTitleDialog.value = t('updateShopGoodsAttr')
attrValueTitleDialog.value = t('updateShopGoodsAttr');
actionAttrValueId.value = data.attr_value_id
Object.assign(formAttrValueData,cloneDeep(data))
showDialogByAttrValue.value = true
showDialogByAttrValue.value = true;
}
//
const formRulesByAttrValue = computed(() => {
return {
attr_value_name: [
{
required: true,
message: t('attrValueNamePlaceholder'),
trigger: 'blur',
},
],
{ required: true, message: t('attrValueNamePlaceholder'), trigger: 'blur' }
]
}
})
@ -554,19 +380,13 @@ const confirmAttrValue = async (formEl: FormInstance | undefined) => {
if (valid) {
if(formAttrValueData.type != 'text'){
if(formAttrValueData.child.length == 0){
ElMessage({
type: 'warning',
message: `${t('attrValueNamePlaceholder')}`,
})
return
ElMessage({type: 'warning', message: `${t('attrValueNamePlaceholder')}`})
return;
}
for(let i=0;i<formAttrValueData.child.length;i++){
if(formAttrValueData.child[i].name == ''){
ElMessage({
type: 'warning',
message: `${t('attrValueNamePlaceholder')}`,
})
break
ElMessage({type: 'warning', message: `${t('attrValueNamePlaceholder')}`})
break;
}
}
}
@ -576,34 +396,29 @@ const confirmAttrValue = async (formEl: FormInstance | undefined) => {
formAttrValueData.child.sort((a: any, b: any) => {
return b.sort - a.sort
})
});
loadingByAttrValue.value = true
if(actionAttrValueId.value == -1) {
attrInfo.attr_value_format.push(formAttrValueData)
}else{
for (let i = 0; i < attrInfo.attr_value_format.length; i++) {
if (
attrInfo.attr_value_format[i].attr_value_id ==
actionAttrValueId.value
) {
if (attrInfo.attr_value_format[i].attr_value_id == actionAttrValueId.value) {
attrInfo.attr_value_format[i] = cloneDeep(formAttrValueData)
break
break;
}
}
}
let data = {
attr_id : attrId.value,
attr_value_format: JSON.stringify(attrInfo.attr_value_format),
}
modifyAttrValue(data)
.then((res) => {
attr_value_format : JSON.stringify(attrInfo.attr_value_format)
};
modifyAttrValue(data).then(res => {
loadingByAttrValue.value = false
showDialogByAttrValue.value = false
repeatAttrValue.value = false
loadAttrInfo()
})
.catch((err) => {
}).catch(err => {
loadingByAttrValue.value = false
repeatAttrValue.value = false
})
@ -613,8 +428,9 @@ const confirmAttrValue = async (formEl: FormInstance | undefined) => {
//
const deleteAttrValueEvent = (item:any,index:any)=> {
formAttrValueData.child.splice(index, 1)
formAttrValueData.child.splice(index,1);
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

130
admin/src/addon/shop/views/goods/brand_list.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="addEvent">
@ -8,109 +9,57 @@
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="brandTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="brandTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('brandName')" prop="brand_name">
<el-input
v-model.trim="brandTable.searchParam.brand_name"
:placeholder="t('brandNamePlaceholder')"
/>
<el-input v-model.trim="brandTable.searchParam.brand_name" :placeholder="t('brandNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadBrandList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadBrandList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="brandTable.data"
size="large"
v-loading="brandTable.loading"
@sort-change="sortChange"
>
<el-table :data="brandTable.data" size="large" v-loading="brandTable.loading" @sort-change="sortChange">
<template #empty>
<span>{{ !brandTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="brand_name"
:label="t('brandName')"
min-width="120"
/>
<el-table-column prop="brand_name" :label="t('brandName')" min-width="120" />
<el-table-column :label="t('logo')" min-width="120">
<template #default="{ row }">
<div class="h-[30px]">
<el-image
class="w-[30px] h-[30px]"
:src="img(row.logo)"
fit="contain"
>
<el-image class="w-[30px] h-[30px] " :src="img(row.logo)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[30px] h-[30px]"
src="@/addon/shop/assets/brand_default.png"
/>
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/brand_default.png" />
</div>
</template>
</el-image>
</div>
</template>
</el-table-column>
<el-table-column
prop="sort"
:label="t('sort')"
min-width="120"
sortable="custom"
>
<el-table-column prop="sort" :label="t('sort')" min-width="120" sortable="custom">
<template #default="{ row }">
<el-input
v-model.trim="row.sort"
class="!w-[100px]"
maxlength="8"
@blur="sortInputListener(row.sort, row)"
/>
<el-input v-model.trim="row.sort" class="!w-[100px]" maxlength="8" @blur="sortInputListener(row.sort, row)" />
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.brand_id)"
>{{ t('delete') }}
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.brand_id)">{{ t('delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="brandTable.page"
v-model:page-size="brandTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="brandTable.total"
@size-change="loadBrandList()"
@current-change="loadBrandList"
/>
<el-pagination v-model:current-page="brandTable.page" v-model:page-size="brandTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="brandTable.total"
@size-change="loadBrandList()" @current-change="loadBrandList" />
</div>
</div>
@ -122,11 +71,7 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getBrandPageList,
deleteBrand,
modifyBrandSort,
} from '@/addon/shop/api/goods'
import { getBrandPageList, deleteBrand, modifyBrandSort } from '@/addon/shop/api/goods'
import { img, debounce } from '@/utils/common'
import { ElMessageBox, FormInstance, ElMessage } from 'element-plus'
import BrandEdit from '@/addon/shop/views/goods/components/brand-edit.vue'
@ -144,8 +89,8 @@ const brandTable = reactive({
searchParam: {
brand_name: '',
order: '',
sort: '',
},
sort: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -175,14 +120,12 @@ const loadBrandList = (page: number = 1) => {
getBrandPageList({
page: brandTable.page,
limit: brandTable.limit,
...brandTable.searchParam,
})
.then((res) => {
...brandTable.searchParam
}).then(res => {
brandTable.loading = false
brandTable.data = res.data.data
brandTable.total = res.data.total
})
.catch(() => {
}).catch(() => {
brandTable.loading = false
})
}
@ -211,16 +154,17 @@ const editEvent = (data: any) => {
* 删除商品品牌
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('brandDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('brandDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteBrand(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteBrand(id).then(() => {
loadBrandList()
}).catch(() => {
})
.catch(() => {})
})
}
//
@ -228,7 +172,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !/^\d{0,8}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${t('sortTips')}`,
message: `${ t('sortTips') }`
})
return
}
@ -237,8 +181,9 @@ const sortInputListener = debounce((sort, row) => {
}
modifyBrandSort({
brand_id: row.brand_id,
sort,
}).then((res) => {})
sort
}).then((res) => {
})
})
const resetForm = (formEl: FormInstance | undefined) => {
@ -248,4 +193,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

135
admin/src/addon/shop/views/goods/category.vue

@ -7,29 +7,12 @@
{{ t('addCategory') }}
</el-button>
</div>
<el-tabs
class="demo-tabs"
model-value="/shop/goods/category"
@tab-change="handleClick"
>
<el-tab-pane
:label="t('tabGoodsCategory')"
name="/shop/goods/category"
/>
<el-tab-pane
:label="t('tabGoodsCategoryConfig')"
name="/shop/goods/category/config"
/>
<el-tabs class="demo-tabs" model-value="/shop/goods/category" @tab-change="handleClick">
<el-tab-pane :label="t('tabGoodsCategory')" name="/shop/goods/category" />
<el-tab-pane :label="t('tabGoodsCategoryConfig')" name="/shop/goods/category/config" />
</el-tabs>
<div class="mt-[10px]">
<el-table
:data="categoryTable.data"
ref="tableRef"
size="large"
v-loading="categoryTable.loading"
row-key="category_id"
:tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }"
>
<el-table :data="categoryTable.data" ref="tableRef" size="large" v-loading="categoryTable.loading" row-key="category_id" :tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }">
<template #empty>
<span>{{ !categoryTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -42,17 +25,10 @@
<el-table-column :label="t('image')" width="170" align="left">
<template #default="{ row }">
<div class="h-[30px]">
<el-image
class="w-[30px] h-[30px]"
:src="img(row.image)"
fit="contain"
>
<el-image class="w-[30px] h-[30px] " :src="img(row.image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[30px] h-[30px]"
src="@/addon/shop/assets/category_default.png"
/>
<img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" />
</div>
</template>
</el-image>
@ -61,33 +37,18 @@
</el-table-column>
<el-table-column prop="is_show" :label="t('isShow')" width="400">
<template #default="{ row }">
<el-tag
class="cursor-pointer"
:type="row.is_show != 2 ? 'success' : 'danger'"
@click="showClick(row)"
>{{ row.is_show != 2 ? '是' : '否' }}</el-tag
>
<el-tag class="cursor-pointer" :type="row.is_show != 2 ? 'success' : 'danger'" @click="showClick(row)">{{ row.is_show != 2 ? '' : '' }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="sort" :label="t('sort')" width="120" />
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
width="200"
>
<el-table-column :label="t('operation')" fixed="right" align="right" width="200">
<template #default="{ row }">
<el-button type="primary" link @click="spreadEvent(row)">{{
t('spreadGoodsCategory')
}}</el-button>
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="deleteEvent(row)">{{
t('delete')
}}</el-button>
<el-button type="primary" link @click="spreadEvent(row)">{{ t('spreadGoodsCategory') }}</el-button>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
</div>
@ -96,18 +57,14 @@
<!-- 商品分类推广弹出框 -->
<goods-category-spread-popup ref="goodsCategorySpreadPopupRef" />
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted, nextTick } from 'vue'
import { t } from '@/lang'
import {
getCategoryTree,
deleteCategory,
updateCategory,
editCategory,
} from '@/addon/shop/api/goods'
import { getCategoryTree, deleteCategory, updateCategory, editCategory } from '@/addon/shop/api/goods'
import { img } from '@/utils/common'
import { ElMessageBox } from 'element-plus'
import categoryEdit from '@/addon/shop/views/goods/components/category-edit.vue'
@ -122,7 +79,7 @@ const pageName = route.meta.title
const tableRef = useTemplateRefsList<HTMLElement>()
const categoryTable = reactive({
loading: true,
data: [],
data: []
})
onMounted(() => {
@ -136,25 +93,21 @@ onMounted(() => {
const activeRows = ref<any[]>([])
//
const rowDrop = () => {
const tbody = tableRef.value.$el.querySelector(
'.el-table__body-wrapper tbody'
)
const tbody = tableRef.value.$el.querySelector('.el-table__body-wrapper tbody')
Sortable.create(tbody, {
handle: '.vues-rank',
animation: 300,
onMove: ({ dragged, related }) => {
const oldRow = activeRows.value[dragged.rowIndex] //
const newRow = activeRows.value[related.rowIndex] //
if (oldRow.pid !== newRow.pid) {
// id
if (oldRow.pid !== newRow.pid) { // id
return false //
}
},
onStart: () => {
//
onStart: () => { //
activeRows.value = treeToTile(cloneDeep(categoryTable.data)) //
},
onEnd: (e) => {
onEnd: e => {
const oldRow = activeRows.value[e.oldIndex] //
const newRow = activeRows.value[e.newIndex] //
@ -167,35 +120,25 @@ const rowDrop = () => {
const currRow = activeRows.value.splice(e.oldIndex, 1)[0]
activeRows.value.splice(e.newIndex, 0, currRow)
const pid = newRow.pid
const currentRows = activeRows.value
.filter((c) => c.pid === pid)
?.map((item, index) => {
const currentRows = activeRows.value.filter(c => c.pid === pid)?.map((item, index) => {
if (item.level === 1 && item.category_id === currRow.category_id) {
categoryTable.data = categoryTable.data.filter(
(c) => c.category_id !== currRow.category_id
)
categoryTable.data = categoryTable.data.filter(c => c.category_id !== currRow.category_id)
categoryTable.data.splice(index, 0, currRow)
}
if (item.level === 2 && item.category_id === currRow.category_id) {
const treeIndex = categoryTable.data.findIndex(
(el) => el.category_id === item.pid
)
const obj = cloneDeep(
categoryTable.data[treeIndex].child_list.filter(
(c) => c.category_id !== currRow.category_id
)
)
const treeIndex = categoryTable.data.findIndex(el => el.category_id === item.pid)
const obj = cloneDeep(categoryTable.data[treeIndex].child_list.filter(c => c.category_id !== currRow.category_id))
categoryTable.data[treeIndex].child_list = []
categoryTable.data[treeIndex].child_list.push(...obj)
categoryTable.data[treeIndex].child_list.splice(index, 0, currRow)
}
return {
category_id: item.category_id, //
sort: 9999 - index,
sort: 9999 - index
}
})
updateCategoryFn({ category_sort_array: currentRows })
},
}
})
}
@ -209,9 +152,7 @@ const treeToTile = (treeData: any, childKey = 'child_list') => {
const arr:Array<any> = []
const expanded = (data:any) => {
if (data && data.length > 0) {
data
.filter((d: any) => d)
.forEach((e: any) => {
data.filter((d:any) => d).forEach((e:any) => {
arr.push(e)
expanded(e[childKey] || [])
})
@ -227,17 +168,15 @@ const treeToTile = (treeData: any, childKey = 'child_list') => {
const loadCategoryList = () => {
categoryTable.loading = true
getCategoryTree()
.then((res) => {
getCategoryTree().then(res => {
categoryTable.loading = false
categoryTable.data = res.data
})
.catch(() => {
}).catch(() => {
categoryTable.loading = false
})
}
const updateCategoryFn = (params: any) => {
updateCategory(params).then((res) => {
updateCategory(params).then(res => {
loadCategoryList()
})
}
@ -271,22 +210,17 @@ const editEvent = (data: any) => {
* 删除商品分类
*/
const deleteEvent = (row: any) => {
ElMessageBox.confirm(
!row.child_list || !row.child_list.length
? t('categoryDeleteTips')
: t('categoryDeleteTips1'),
t('warning'),
ElMessageBox.confirm(!row.child_list || !row.child_list.length ? t('categoryDeleteTips') : t('categoryDeleteTips1'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
type: 'warning'
}
).then(() => {
deleteCategory(row.category_id)
.then(() => {
deleteCategory(row.category_id).then(() => {
loadCategoryList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -302,6 +236,7 @@ const router = useRouter()
const handleClick = (path: string) => {
router.push({ path })
}
</script>
<style lang="scss" scoped>
@ -315,7 +250,9 @@ const handleClick = (path: string) => {
.el-table__placeholder {
order: 1;
}
}
}
}
</style>

183
admin/src/addon/shop/views/goods/category_config.vue

@ -3,79 +3,32 @@
<div class="flex ml-[18px] justify-between items-center mb-[5px]">
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-tabs
class="demo-tabs mx-[18px]"
model-value="/shop/goods/category/config"
@tab-change="handleClick"
>
<el-tabs class="demo-tabs mx-[18px]" model-value="/shop/goods/category/config" @tab-change="handleClick">
<el-tab-pane :label="t('tabGoodsCategory')" name="/shop/goods/category" />
<el-tab-pane
:label="t('tabGoodsCategoryConfig')"
name="/shop/goods/category/config"
/>
<el-tab-pane :label="t('tabGoodsCategoryConfig')" name="/shop/goods/category/config" />
</el-tabs>
<el-form
v-if="Object.keys(formData).length"
:model="formData"
label-width="170"
ref="formRef"
:rules="rules"
class="page-form"
v-loading="loading"
>
<el-form v-if="Object.keys(formData).length" :model="formData" label-width="170" ref="formRef" :rules="rules" class="page-form" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-[16px] pl-[15px]">
{{ t('categoryTemplate') }}
</h3>
<h3 class="panel-title !text-[16px] pl-[15px]">{{ t('categoryTemplate') }}</h3>
<el-form-item :label="t('categoryType')">
<el-radio-group
class="mx-[10px]"
v-model="formData.level"
@change="formData.template = 'style-1'"
>
<el-radio-group class="mx-[10px]" v-model="formData.level" @change="formData.template='style-1'">
<el-radio :label="1">{{ t('categorystyleOne') }}</el-radio>
<el-radio :label="2">{{ t('categorystyleTwo') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('categoryTemplate')">
<template
v-for="(item, index) in config['level_' + formData.level]"
:key="index"
>
<div
:class="[
'w-[150px] border-[1px] border-[#ededed] border-solid overflow-hidden text-[#ededed] rounded-[4px] mr-[20px] relative',
formData.template === item.template
? 'border-color text-color'
: '',
]"
@click="levelChange(item.template)"
>
<img
class="w-[100%]"
:src="img(item.preview)"
fit-object="contain"
/>
<span
class="iconfont iconicon-selected absolute right-0 bottom-[-8px]"
></span>
<template v-for="(item,index) in config['level_'+formData.level]" :key="index">
<div :class="['w-[150px] border-[1px] border-[#ededed] border-solid overflow-hidden text-[#ededed] rounded-[4px] mr-[20px] relative', formData.template ===item.template ? 'border-color text-color' : '']" @click="levelChange(item.template)">
<img class="w-[100%]" :src="img(item.preview)" fit-object="contain" />
<span class="iconfont iconicon-selected absolute right-0 bottom-[-8px]"></span>
</div>
</template>
</el-form-item>
</el-card>
<el-card class="box-card !border-none" shadow="never">
<h3 class="panel-title !text-[16px] pl-[15px]">
{{ t('pageSettings') }}
</h3>
<h3 class="panel-title !text-[16px] pl-[15px]">{{ t('pageSettings') }}</h3>
<el-form-item :label="t('pageTitle')" prop="page_title">
<el-input
v-model.trim="formData.page_title"
clearable
:placeholder="t('pageTitlePlaceholder')"
class="input-width"
maxlength="10"
show-word-limit
/>
<el-input v-model.trim="formData.page_title" clearable :placeholder="t('pageTitlePlaceholder')" class="input-width" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('searchControl')">
<el-radio-group class="mx-[10px]" v-model="formData.search.control">
@ -83,26 +36,10 @@
<el-radio :label="0">{{ t('close') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="formData.search.control"
:label="t('searchTitle')"
prop="search.title"
>
<el-input
v-model.trim="formData.search.title"
clearable
:placeholder="t('searchTitlePlaceholder')"
class="input-width"
maxlength="12"
show-word-limit
/>
<el-form-item v-if="formData.search.control" :label="t('searchTitle')" prop="search.title">
<el-input v-model.trim="formData.search.title" clearable :placeholder="t('searchTitlePlaceholder')" class="input-width" maxlength="12" show-word-limit />
</el-form-item>
<template
v-if="
formData.level != 2 ||
(formData.level === 2 && formData.template != 'style-1')
"
>
<template v-if="formData.level!=2||(formData.level===2&&formData.template != 'style-1')">
<!-- <el-form-item :label="t('sort')" prop="sort">
<el-select v-model="formData.sort" clearable :placeholder="t('sortPlaceholder')"
class="input-width">
@ -126,16 +63,8 @@
<template v-if="formData.cart.control">
<el-form-item :label="t('cartStyle')" class="carStyle">
<div class="flex items-center">
<div
:class="[
'flex items-center justify-center w-[65px] h-[65px] border-0 border-color rounded-[4px] border-solid box-border cursor-pointer mr-[15px]',
formData.cart.style === 'style-1' ? '!border-[1px]' : '',
]"
@click="carStyleClick(1)"
>
<div
class="text-[#fff] bg-color h-[20px] text-[12px] px-[10px] leading-[20px] rounded-[10px]"
>
<div :class="['flex items-center justify-center w-[65px] h-[65px] border-0 border-color rounded-[4px] border-solid box-border cursor-pointer mr-[15px]', formData.cart.style === 'style-1' ? '!border-[1px]' : '']" @click="carStyleClick(1)">
<div class="text-[#fff] bg-color h-[20px] text-[12px] px-[10px] leading-[20px] rounded-[10px]">
{{ formData.cart.text }}
</div>
</div>
@ -145,46 +74,18 @@
<CirclePlusFilled />
</el-icon>
</div> -->
<div
:class="[
'flex items-center justify-center w-[65px] h-[65px] border-0 border-color rounded-[4px] border-solid box-border cursor-pointer mr-[15px]',
formData.cart.style === 'style-3' ? '!border-[1px]' : '',
]"
@click="carStyleClick(3)"
>
<span
class="text-color nc-iconfont nc-icon-gouwucheV6xx6 !text-[24px]"
></span>
<div :class="['flex items-center justify-center w-[65px] h-[65px] border-0 border-color rounded-[4px] border-solid box-border cursor-pointer mr-[15px]', formData.cart.style === 'style-3' ? '!border-[1px]' : '']" @click="carStyleClick(3)">
<span class="text-color nc-iconfont nc-icon-gouwucheV6xx6 !text-[24px]"></span>
</div>
<div
:class="[
'flex items-center justify-center w-[65px] h-[65px] border-0 border-color rounded-[4px] border-solid box-border cursor-pointer mr-[15px]',
formData.cart.style === 'style-4' ? '!border-[1px]' : '',
]"
@click="carStyleClick(4)"
>
<div
class="text-[#fff] bg-color h-[30px] w-[30px] leading-[30px] rounded-[30px] text-center"
>
<span
class="nc-iconfont nc-icon-gouwucheV6xx6 !text-[20px]"
></span>
<div :class="['flex items-center justify-center w-[65px] h-[65px] border-0 border-color rounded-[4px] border-solid box-border cursor-pointer mr-[15px]', formData.cart.style === 'style-4' ? '!border-[1px]' : '']" @click="carStyleClick(4)">
<div class="text-[#fff] bg-color h-[30px] w-[30px] leading-[30px] rounded-[30px] text-center">
<span class="nc-iconfont nc-icon-gouwucheV6xx6 !text-[20px]"></span>
</div>
</div>
</div>
</el-form-item>
<el-form-item
v-if="formData.cart.style === 'style-1'"
prop="cart.text"
>
<el-input
v-model.trim="formData.cart.text"
clearable
:placeholder="t('cartTextPlaceholder')"
class="input-width"
maxlength="3"
show-word-limit
/>
<el-form-item v-if="formData.cart.style === 'style-1'" prop="cart.text">
<el-input v-model.trim="formData.cart.text" clearable :placeholder="t('cartTextPlaceholder')" class="input-width" maxlength="3" show-word-limit />
</el-form-item>
<el-form-item :label="t('cartEvent')">
<el-radio-group class="mx-[10px]" v-model="formData.cart.event">
@ -198,9 +99,7 @@
</el-form>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
</div>
</div>
</div>
@ -236,17 +135,17 @@ const loading = ref(false)
const rules = computed(() => {
return {
page_title: [
{ required: true, message: t('pageTitlePlaceholder'), trigger: 'blur' },
{ required: true, message: t('pageTitlePlaceholder'), trigger: 'blur' }
],
'search.title': [
{ required: true, message: t('searchTitlePlaceholder'), trigger: 'blur' },
{ required: true, message: t('searchTitlePlaceholder'), trigger: 'blur' }
],
sort: [
{ required: true, message: t('sortPlaceholder'), trigger: 'change' },
{ required: true, message: t('sortPlaceholder'), trigger: 'change' }
],
'cart.text': [
{ required: true, message: t('cartTextPlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('cartTextPlaceholder'), trigger: 'blur' }
]
}
})
@ -264,28 +163,26 @@ const config = reactive<configType | any>({
level_1: [
{
template: 'style-1',
preview: 'addon/shop/category_style1_1.png',
},
preview: 'addon/shop/category_style1_1.png'
}
],
level_2: [
{
template: 'style-1',
preview: 'addon/shop/category_style2_1.png',
preview: 'addon/shop/category_style2_1.png'
},
{
template: 'style-2',
preview: 'addon/shop/category_style2_2.png',
},
],
preview: 'addon/shop/category_style2_2.png'
}
]
})
const getCategoryConfigFn = () => {
loading.value = true
getCategoryConfig()
.then((res) => {
getCategoryConfig().then(res => {
formData.value = res.data
loading.value = false
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
@ -301,11 +198,9 @@ const onSave = async (formEl: any) => {
await formEl.validate(async (valid:any) => {
if (valid) {
loading.value = true
setCategoryConfig(formData.value)
.then((res) => {
setCategoryConfig(formData.value).then(res => {
getCategoryConfigFn()
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}

77
admin/src/addon/shop/views/goods/components/brand-edit.vue

@ -1,28 +1,8 @@
<template>
<el-dialog
v-model="showDialog"
:title="title"
width="500px"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-dialog v-model="showDialog" :title="title" width="500px" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('brandName')" prop="brand_name">
<el-input
v-model.trim="formData.brand_name"
clearable
:placeholder="t('brandNamePlaceholder')"
class="input-width"
maxlength="10"
@blur="formData.brand_name = $event.target.value"
/>
<el-input v-model.trim="formData.brand_name" clearable :placeholder="t('brandNamePlaceholder')" class="input-width" maxlength="10" @blur="formData.brand_name = $event.target.value"/>
</el-form-item>
<el-form-item :label="t('logo')">
@ -30,39 +10,18 @@
</el-form-item>
<el-form-item :label="t('desc')" >
<el-input
v-model.trim="formData.desc"
type="textarea"
clearable
:placeholder="t('descPlaceholder')"
class="input-width"
maxlength="200"
/>
<el-input v-model.trim="formData.desc" type="textarea" clearable :placeholder="t('descPlaceholder')" class="input-width" maxlength="200"/>
</el-form-item>
<el-form-item :label="t('sort')" >
<el-input
v-model.trim="formData.sort"
maxlength="8"
show-word-limit
clearable
:placeholder="t('sortPlaceholder')"
class="input-width"
@keyup="filterNumber($event)"
@blur="formData.sort = $event.target.value"
/>
<el-input v-model.trim="formData.sort" maxlength="8" show-word-limit clearable :placeholder="t('sortPlaceholder')" class="input-width" @keyup="filterNumber($event)" @blur="formData.sort = $event.target.value"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -88,7 +47,7 @@ const initialFormData = {
brand_name: '',
logo: '',
desc: '',
sort: '',
sort: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -98,8 +57,8 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
brand_name: [
{ required: true, message: t('brandNamePlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('brandNamePlaceholder'), trigger: 'blur' }
]
}
})
@ -119,13 +78,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
const data = formData
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
}).catch(err => {
loading.value = false
})
}
@ -152,19 +109,13 @@ const setFormData = async (row: any = null) => {
}
const filterSpecial = (event:any) => {
event.target.value = event.target.value.replace(
/[^\u4e00-\u9fa5a-zA-Z0-9]/g,
''
)
event.target.value = event.target.value.replace(
/[`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g,
''
)
event.target.value = event.target.value.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '')
event.target.value = event.target.value.replace(/[`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g, '')
}
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>

92
admin/src/addon/shop/views/goods/components/category-edit.vue

@ -1,44 +1,13 @@
<template>
<el-dialog
v-model="showDialog"
:title="title"
width="480"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-dialog v-model="showDialog" :title="title" width="480" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('categoryName')" prop="category_name">
<el-input
v-model.trim="formData.category_name"
clearable
:placeholder="t('categoryNamePlaceholder')"
class="input-width"
maxlength="10"
show-word-limit
/>
<el-input v-model.trim="formData.category_name" clearable :placeholder="t('categoryNamePlaceholder')" class="input-width" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('pid')" prop="pid">
<el-select
v-model="formData.pid"
clearable
:disabled="formData.child_count"
:placeholder="t('pidPlaceholder')"
class="input-width"
>
<el-select v-model="formData.pid" clearable :disabled="formData.child_count" :placeholder="t('pidPlaceholder')" class="input-width">
<el-option label="顶级分类" :value="0" />
<el-option
v-for="item in optionList"
:key="item.category_id"
:label="item.category_name"
:value="item.category_id"
/>
<el-option v-for="(item) in optionList" :key="item.category_id" :label="item.category_name" :value="item.category_id" />
</el-select>
</el-form-item>
<el-form-item :label="t('image')">
@ -46,24 +15,14 @@
</el-form-item>
<el-form-item :label="t('isShow')" prop="is_show">
<el-switch
v-model="formData.is_show"
class="input-width"
:active-value="1"
:inactive-value="2"
/>
<el-switch v-model="formData.is_show" class="input-width" :active-value="1" :inactive-value="2" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -73,12 +32,7 @@
import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import {
addCategory,
editCategory,
getCategoryInfo,
getCategoryList,
} from '@/addon/shop/api/goods'
import { addCategory, editCategory, getCategoryInfo, getCategoryList } from '@/addon/shop/api/goods'
const showDialog = ref(false)
const loading = ref(false)
@ -95,7 +49,7 @@ const initialFormData = {
is_show: 1,
child_count: 0,
// sort: 9999,
level: 1,
level: 1
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -105,16 +59,14 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
category_id: [
{ required: true, message: t('categoryIdPlaceholder'), trigger: 'blur' },
{ required: true, message: t('categoryIdPlaceholder'), trigger: 'blur' }
],
category_name: [
{
required: true,
message: t('categoryNamePlaceholder'),
trigger: 'blur',
},
{ required: true, message: t('categoryNamePlaceholder'), trigger: 'blur' }
],
pid: [{ required: true, message: t('pidPlaceholder'), trigger: 'change' }],
pid: [
{ required: true, message: t('pidPlaceholder'), trigger: 'change' }
]
}
})
@ -139,13 +91,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
const data = formData
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
}).catch(err => {
loading.value = false
})
}
@ -155,11 +105,9 @@ const confirm = async (formEl: FormInstance | undefined) => {
//
const getCategoryAllFn = () => {
getCategoryList({
level: 1,
}).then((res) => {
optionList.value = res.data.filter(
(el: any) => el.category_id != formData.category_id
)
level: 1
}).then(res => {
optionList.value = res.data.filter((el: any) => el.category_id != formData.category_id)
})
}
@ -184,7 +132,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>

151
admin/src/addon/shop/views/goods/components/coupon-select-popup.vue

@ -3,59 +3,26 @@
<div @click="show">
<slot>
<el-button>{{ t('选择优惠券') }}</el-button>
<div
class="inline-block ml-[10px] text-[14px]"
v-show="couponIds.length"
>
<div class="inline-block ml-[10px] text-[14px]" v-show="couponIds.length">
<span>{{ t('已选') }}</span>
<span class="text-primary mx-[2px]">{{ couponIds.length }}</span>
<span>{{ t('张') }}</span>
</div>
</slot>
</div>
<el-dialog
v-model="showDialog"
:title="t('优惠券选择')"
width="1000px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-form
:inline="true"
:model="couponTable.searchParam"
ref="searchFormRef"
>
<el-form-item
:label="t('优惠券名称')"
prop="keyword"
class="form-item-wrap"
>
<el-input
v-model.trim="couponTable.searchParam.title"
:placeholder="t('请输入优惠券名称')"
maxlength="60"
/>
<el-dialog v-model="showDialog" :title="t('优惠券选择')" width="1000px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-form :inline="true" :model="couponTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('优惠券名称')" prop="keyword" class="form-item-wrap">
<el-input v-model.trim="couponTable.searchParam.title" :placeholder="t('请输入优惠券名称')" maxlength="60" />
</el-form-item>
<el-form-item class="form-item-wrap">
<el-button type="primary" @click="loadCouponList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadCouponList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-table
:data="couponTable.data"
size="large"
v-loading="couponTable.loading"
ref="couponListTableRef"
max-height="400"
@select="handleSelectChange"
@select-all="handleSelectAllChange"
>
<el-table :data="couponTable.data" size="large" v-loading="couponTable.loading" ref="couponListTableRef" max-height="400" @select="handleSelectChange" @select-all="handleSelectAllChange">
<template #empty>
<span>{{ !couponTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -76,39 +43,24 @@
</el-table-column>
<el-table-column :label="t('有效期')" min-width="210">
<template #default="{ row }">
<span v-if="row.valid_type == 1">
领取之日起{{ row.length || '' }} 天内有效</span
>
<span v-if="row.valid_type == 1"> 领取之日起{{ row.length || '' }} 天内有效</span>
<span v-else> 使用截止时间至{{ row.valid_end_time || ''}} </span>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex">
<div class="flex items-center flex-1">
<div
class="layui-table-bottom-left-container mr-[10px]"
v-show="selectCouponNum"
>
<div class="layui-table-bottom-left-container mr-[10px]" v-show="selectCouponNum">
<span>{{ t('已选择') }}</span>
<span class="text-primary mx-[2px]">{{ selectCouponNum }}</span>
<span>{{ t('张优惠券') }}</span>
</div>
<el-button
type="primary"
link
@click="clear"
v-show="selectCouponNum"
>{{ t('取消选择') }}</el-button
>
<el-button type="primary" link @click="clear" v-show="selectCouponNum">{{ t('取消选择') }}</el-button>
</div>
<el-pagination
v-model:current-page="couponTable.page"
v-model:page-size="couponTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="couponTable.total"
@size-change="loadCouponList()"
@current-change="loadCouponList"
/>
<el-pagination v-model:current-page="couponTable.page" v-model:page-size="couponTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="couponTable.total"
@size-change="loadCouponList()" @current-change="loadCouponList" />
</div>
<template #footer>
@ -131,16 +83,16 @@ import { getCouponSelectList } from '@/addon/shop/api/marketing'
const prop = defineProps({
modelValue: {
type: String,
default: '',
default: ''
},
max: {
type: Number,
default: 0,
default: 0
},
min: {
type: Number,
default: 0,
},
default: 0
}
})
const emit = defineEmits(['update:modelValue','couponSelect'])
@ -151,7 +103,7 @@ const couponIds: any = computed({
},
set (value) {
emit('update:modelValue', value)
},
}
})
const showDialog = ref(false)
@ -174,13 +126,14 @@ const couponTable = reactive({
title: '',
status: 1,
verify_coupon_ids:'',
},
}
})
const searchFormRef = ref()
//
const initData = () => {}
const initData = () => {
}
initData()
@ -228,37 +181,36 @@ const loadCouponList = (page: number = 1, callback: any = null) => {
couponTable.loading = true
couponTable.page = page
const searchData: any = cloneDeep(couponTable.searchParam)
const searchData: any = cloneDeep(couponTable.searchParam);
getCouponSelectList({
page: couponTable.page,
limit: couponTable.limit,
...searchData,
})
.then((res) => {
...searchData
}).then(res => {
couponTable.loading = false
couponTable.data = res.data.data
couponTable.total = res.data.total
if (callback) callback(res.data.verify_coupon_ids)
setCouponSelected()
})
.catch(() => {
setCouponSelected();
}).catch(() => {
couponTable.loading = false
})
}
//
const setCouponSelected = () => {
nextTick(() => {
if (!couponListTableRef.value) return
if (!couponListTableRef.value) return;
for (let i = 0; i < couponTable.data.length; i++) {
couponListTableRef.value.toggleRowSelection(couponTable.data[i], false)
couponListTableRef.value.toggleRowSelection(couponTable.data[i], false);
if (selectCoupon['coupon_' + couponTable.data[i].id]) {
couponListTableRef.value.toggleRowSelection(couponTable.data[i], true)
couponListTableRef.value.toggleRowSelection(couponTable.data[i], true);
}
}
})
});
}
const resetForm = (formEl: FormInstance | undefined) => {
@ -270,28 +222,28 @@ const resetForm = (formEl: FormInstance | undefined) => {
const show = () => {
// idid
couponTable.searchParam.verify_coupon_ids = couponIds.value
couponTable.searchParam.verify_coupon_ids = couponIds.value;
loadCouponList(1, (verify_coupon_ids:any) => {
//
if (couponIds.value) {
couponIds.value.splice(0, couponIds.value.length, ...verify_coupon_ids)
// selectCoupon couponIds
for (let key in selectCoupon) {
const couponId = key.replace('coupon_', '')
const couponId = key.replace('coupon_', '');
if (!couponIds.value.includes(Number(couponId))) {
delete selectCoupon[key] //
delete selectCoupon[key]; //
}
}
couponIds.value.forEach((item: any) => {
if (!selectCoupon['coupon_' + item]) {
selectCoupon['coupon_' + item] = {}
selectCoupon['coupon_' + item] = {};
}
})
//
for (let i = 0; i < couponTable.data.length; i++) {
if (couponIds.value.indexOf(couponTable.data[i].id) != -1) {
selectCoupon['coupon_' + couponTable.data[i].id] = couponTable.data[i]
selectCoupon['coupon_' + couponTable.data[i].id] = couponTable.data[i];
}
}
}
@ -302,9 +254,9 @@ const show = () => {
//
const clear = () => {
for (let k in selectCoupon) {
delete selectCoupon[k]
delete selectCoupon[k];
}
setCouponSelected()
setCouponSelected();
}
const save = () => {
@ -312,26 +264,21 @@ const save = () => {
ElMessage({
type: 'warning',
message: `${t('所选优惠券数量不能少于')}${prop.min}${t('张')}`,
})
return
});
return;
}
if (
prop.max &&
prop.max > 0 &&
selectCouponNum.value &&
selectCouponNum.value > prop.max
) {
if (prop.max && prop.max > 0 && selectCouponNum.value && selectCouponNum.value > prop.max) {
ElMessage({
type: 'warning',
message: `${t('所选优惠券数量不能超过')}${prop.max}${t('张')}`,
})
return
});
return;
}
let ids: any = []
let ids: any = [];
for (let k in selectCoupon) {
ids.push(parseInt(k.replace('coupon_', '')))
ids.push(parseInt(k.replace('coupon_', '')));
}
couponIds.value.splice(0, couponIds.value.length, ...ids)
@ -342,7 +289,7 @@ const save = () => {
defineExpose({
showDialog,
selectCoupon,
selectCouponNum,
selectCouponNum
})
</script>

92
admin/src/addon/shop/views/goods/components/evaluate-add.vue

@ -1,59 +1,23 @@
<template>
<el-dialog
v-model="showDialog"
:title="formData.evaluate_id ? t('updateEvaluate') : t('addEvaluate')"
width="700px"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-dialog v-model="showDialog" :title="formData.evaluate_id ? t('updateEvaluate') : t('addEvaluate')" width="700px" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('goodsInfo')" prop="goods_id">
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="formData.goods_id"
:min="1"
:max="1"
/>
<goods-select-popup ref="goodsSelectPopupRef" v-model="formData.goods_id" :min="1" :max="1" />
</el-form-item>
<el-form-item :label="t('memberHead')" prop="member_head">
<upload-image v-model="formData.member_head" />
</el-form-item>
<el-form-item :label="t('memberName')" prop="member_name">
<el-input
v-model.trim="formData.member_name"
clearable
maxlength="20"
show-word-limit
:placeholder="t('memberNamePlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.member_name" clearable maxlength="20" show-word-limit :placeholder="t('memberNamePlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<div class="input-width">
<el-date-picker
clearable
:disabled-date="disabledDateFn"
v-model="formData.create_time"
type="datetime"
:placeholder="t('createTimePlaceholder')"
value-format="YYYY-MM-DD HH:mm:ss"
/>
<el-date-picker clearable :disabled-date="disabledDateFn" v-model="formData.create_time" type="datetime" :placeholder="t('createTimePlaceholder')" value-format="YYYY-MM-DD HH:mm:ss" />
</div>
</el-form-item>
<el-form-item :label="t('isAnonymous')" clearable prop="is_anonymous">
<el-radio-group
v-model="formData.is_anonymous"
:placeholder="t('isAnonymousPlaceholder')"
clearable
>
<el-radio-group v-model="formData.is_anonymous" :placeholder="t('isAnonymousPlaceholder')" clearable>
<el-radio :label="1">{{ t('anonymous') }}</el-radio>
<el-radio :label="2">{{ t('notAnonymous') }}</el-radio>
</el-radio-group>
@ -62,33 +26,21 @@
<el-rate v-model="formData.scores" />
</el-form-item>
<el-form-item :label="t('content')" prop="content">
<el-input
v-model.trim="formData.content"
maxlength="200"
show-word-limit
type="textarea"
rows="4"
clearable
:placeholder="t('contentPlaceholder')"
class="input-width"
/>
<el-input v-model.trim="formData.content" maxlength="200" show-word-limit type="textarea" rows="4" clearable :placeholder="t('contentPlaceholder')" class="input-width" />
</el-form-item>
<el-form-item :label="t('images')">
<upload-image v-model="formData.image_arr" :limit="9" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
</template>
@ -118,7 +70,7 @@ const initialFormData = {
image_arr: '',
is_anonymous: 1,
scores: 5,
create_time: '',
create_time: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -129,23 +81,23 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
goods_id: [
{ required: true, message: t('goodsIdPlaceholder'), trigger: 'blur' },
{ required: true, message: t('goodsIdPlaceholder'), trigger: 'blur' }
],
member_head: [
{ required: true, message: t('memberHeadPlaceholder'), trigger: 'blur' },
{ required: true, message: t('memberHeadPlaceholder'), trigger: 'blur' }
],
member_name: [
{ required: true, message: t('memberNamePlaceholder'), trigger: 'blur' },
{ required: true, message: t('memberNamePlaceholder'), trigger: 'blur' }
],
content: [
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' },
{ required: true, message: t('contentPlaceholder'), trigger: 'blur' }
],
images: [
{ required: true, message: t('imagesPlaceholder'), trigger: 'blur' },
{ required: true, message: t('imagesPlaceholder'), trigger: 'blur' }
],
create_time: [
{ required: true, message: t('createTimePlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('createTimePlaceholder'), trigger: 'blur' }
]
}
})
@ -166,13 +118,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
const data = formData
if (data.goods_id.length > 0) data.goods_id = data.goods_id[0]
addEvaluate(data)
.then((res) => {
addEvaluate(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
}).catch(err => {
loading.value = false
})
}
@ -193,7 +143,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>

432
admin/src/addon/shop/views/goods/components/goods-batch-settings-popup.vue

@ -1,30 +1,12 @@
<template>
<el-dialog
v-model="showDialog"
:title="t('batchSetting')"
width="700px"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-dialog v-model="showDialog" :title="t('batchSetting')" width="700px" class="diy-dialog-wrap" :destroy-on-close="true">
<el-alert :title="t('batchSettingTip')" type="info" :closable="false" />
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="mt-[10px] page-form"
v-loading="loading"
>
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="mt-[10px] page-form" v-loading="loading">
<el-row>
<!-- 左侧菜单栏 -->
<el-col :span="4" class="menu-column">
<el-menu :default-active="activeMenu" @select="handleMenuSelect">
<el-menu-item
v-for="[key, value] in Object.entries(setTypeList)"
:key="key"
:index="key"
>{{ value }}</el-menu-item
>
<el-menu-item v-for="([key, value]) in Object.entries(setTypeList)" :key="key" :index="key">{{ value }}</el-menu-item>
</el-menu>
</el-col>
@ -33,157 +15,58 @@
<!-- 商品标签 -->
<el-form-item v-if="activeMenu === 'label'" :label="t('label')">
<el-checkbox-group v-model="formData.label_ids">
<el-checkbox
:label="item.label_id"
v-for="(item, index) in labelOptions"
:key="index"
>{{ item.label_name }}</el-checkbox
>
<el-checkbox :label="item.label_id" v-for="(item, index) in labelOptions" :key="index" >{{ item.label_name }}</el-checkbox>
</el-checkbox-group>
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshGoodsLabel"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toGoodsLabelEvent"
>{{ t('addGoodsLabel') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshGoodsLabel">{{ t('refresh') }}</span>
<span class="cursor-pointer text-primary" @click="toGoodsLabelEvent">{{ t('addGoodsLabel') }}</span>
</div>
</el-form-item>
<!-- 商品服务 -->
<el-form-item v-if="activeMenu === 'service'" :label="t('label')">
<el-checkbox-group v-model="formData.service_ids">
<el-checkbox
:label="item.service_id"
v-for="(item, index) in serviceOptions"
:key="index"
>{{ item.service_name }}</el-checkbox
>
<el-checkbox :label="item.service_id" v-for="(item, index) in serviceOptions" :key="index" >{{ item.service_name }}</el-checkbox>
</el-checkbox-group>
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshGoodsService"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toGoodsServiceEvent"
>{{ t('addGoodsService') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshGoodsService">{{ t('refresh') }}</span>
<span class="cursor-pointer text-primary" @click="toGoodsServiceEvent">{{ t('addGoodsService') }}</span>
</div>
</el-form-item>
<!-- 虚拟销量 -->
<el-form-item
v-if="activeMenu === 'virtual_sale_num'"
:label="t('virtualSaleNum')"
prop="virtual_sale_num"
>
<el-form-item v-if="activeMenu === 'virtual_sale_num'" :label="t('virtualSaleNum')" prop="virtual_sale_num">
<div>
<el-input
v-model.trim="formData.virtual_sale_num"
:placeholder="t('virtualSaleNumPlaceholder')"
class="input-width"
show-word-limit
maxlength="8"
clearable
@keyup="filterNumber($event)"
@blur="formData.virtual_sale_num = $event.target.value"
>
<template #append>{{
formData.unit ? formData.unit : '件'
}}</template>
<el-input v-model.trim="formData.virtual_sale_num" :placeholder="t('virtualSaleNumPlaceholder')" class="input-width" show-word-limit maxlength="8" clearable @keyup="filterNumber($event)" @blur="formData.virtual_sale_num = $event.target.value">
<template #append>{{ formData.unit ? formData.unit : '件' }}</template>
</el-input>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">
{{ t('virtualSaleNumDesc') }}
</div>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">{{ t('virtualSaleNumDesc') }}</div>
</div>
</el-form-item>
<!-- 商品分类 -->
<el-form-item
v-if="activeMenu === 'category'"
prop="goods_category"
:label="t('goodsCategory')"
>
<el-cascader
v-model="formData.goods_category"
:options="goodsCategoryOptions"
:props="goodsCategoryProps"
clearable
filterable
@change="categoryHandleChange"
/>
<el-form-item v-if="activeMenu === 'category'" prop="goods_category" :label="t('goodsCategory')">
<el-cascader v-model="formData.goods_category" :options="goodsCategoryOptions" :props="goodsCategoryProps" clearable filterable @change="categoryHandleChange" />
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshGoodsCategory(true)"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toGoodsCategoryEvent"
>{{ t('addGoodsCategory') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshGoodsCategory(true)">{{ t('refresh') }}</span>
<span class="cursor-pointer text-primary" @click="toGoodsCategoryEvent">{{ t('addGoodsCategory') }}</span>
</div>
</el-form-item>
<!-- 商品品牌 -->
<el-form-item v-if="activeMenu === 'brand'" :label="t('goodsBrand')">
<el-select
v-model="formData.brand_id"
:placeholder="t('brandPlaceholder')"
clearable
>
<el-option
v-for="item in brandOptions"
:key="item.brand_id"
:label="item.brand_name"
:value="item.brand_id"
/>
<el-select v-model="formData.brand_id" :placeholder="t('brandPlaceholder')" clearable>
<el-option v-for="item in brandOptions" :key="item.brand_id" :label="item.brand_name" :value="item.brand_id" />
</el-select>
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshGoodsBrand(true)"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toGoodsBrandEvent"
>{{ t('addGoodsBrand') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshGoodsBrand(true)">{{ t('refresh') }}</span>
<span class="cursor-pointer text-primary" @click="toGoodsBrandEvent">{{ t('addGoodsBrand') }}</span>
</div>
</el-form-item>
<!-- 商品海报 -->
<el-form-item
v-if="activeMenu === 'poster'"
:label="t('goodsPoster')"
>
<el-select
v-model="formData.poster_id"
:placeholder="t('posterPlaceholder')"
clearable
>
<el-option
v-for="item in posterOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
<el-form-item v-if="activeMenu === 'poster'" :label="t('goodsPoster')">
<el-select v-model="formData.poster_id" :placeholder="t('posterPlaceholder')" clearable>
<el-option v-for="item in posterOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshGoodsPoster(true)"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toPosterEvent"
>{{ t('addGoodsPoster') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshGoodsPoster(true)">{{ t('refresh') }}</span>
<span class="cursor-pointer text-primary" @click="toPosterEvent">{{ t('addGoodsPoster') }}</span>
</div>
</el-form-item>
<!-- 是否赠品 -->
@ -193,9 +76,7 @@
<el-radio :label= "1">{{ t('yes') }}</el-radio>
<el-radio :label= "0">{{ t('no') }}</el-radio>
</el-radio-group>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">
{{ t('giftTips') }}
</div>
<div class="mt-[10px] text-[12px] text-[#999] leading-[20px]">{{ t('giftTips') }}</div>
</div>
</el-form-item>
<!-- 配送设置 -->
@ -203,105 +84,35 @@
<el-form-item :label="t('deliveryType')" prop="delivery_type">
<div>
<el-checkbox-group v-model="formData.delivery_type">
<el-checkbox
v-for="(item, index) in deliveryTypeCheckBox"
:key="index"
:label="item.key"
>{{ item.name }}</el-checkbox
>
<el-checkbox v-for="(item, index) in deliveryTypeCheckBox" :key="index" :label="item.key">{{ item.name }}</el-checkbox>
</el-checkbox-group>
<div
v-if="deliveryTypeCheckBox && deliveryTypeCheckBox.length"
class="text-[12px] text-[#999] leading-[20px]"
>
只针对实物商品有效
</div>
<div
v-if="deliveryTypeFlag"
class="text-[12px] text-[#999] leading-[20px]"
>
请先在配置设置中设置配送方式
</div>
<div v-if="deliveryTypeCheckBox && deliveryTypeCheckBox.length" class="text-[12px] text-[#999] leading-[20px]">只针对实物商品有效</div>
<div v-if="deliveryTypeFlag" class="text-[12px] text-[#999] leading-[20px]">请先在配置设置中设置配送方式</div>
</div>
</el-form-item>
<el-form-item
:label="t('isFreeShipping')"
v-show="formData.delivery_type.indexOf('express') != -1"
>
<el-switch
v-model="formData.is_free_shipping"
:active-value="1"
:inactive-value="0"
/>
<el-form-item :label="t('isFreeShipping')" v-show="formData.delivery_type.indexOf('express') != -1">
<el-switch v-model="formData.is_free_shipping" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item
:label="t('feeType')"
prop="fee_type"
v-show="
formData.delivery_type.indexOf('express') != -1 &&
formData.is_free_shipping == 0
"
>
<el-form-item :label="t('feeType')" prop="fee_type" v-show="formData.delivery_type.indexOf('express') != -1 && formData.is_free_shipping == 0">
<el-radio-group v-model="formData.fee_type">
<el-radio label="template">{{ t('selectTemplate') }}</el-radio>
<el-radio label="fixed">{{ t('fixedShipping') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('deliveryMoney')"
prop="delivery_money"
v-show="
formData.delivery_type.indexOf('express') != -1 &&
formData.is_free_shipping == 0 &&
formData.fee_type == 'fixed'
"
>
<el-input
v-model.trim="formData.delivery_money"
clearable
placeholder="0.00"
class="input-width-short"
maxlength="8"
>
<el-form-item :label="t('deliveryMoney')" prop="delivery_money" v-show="formData.delivery_type.indexOf('express') != -1 && formData.is_free_shipping == 0 && formData.fee_type == 'fixed'">
<el-input v-model.trim="formData.delivery_money" clearable placeholder="0.00" class="input-width-short" maxlength="8">
<template #append>{{ t('yuan') }}</template>
</el-input>
</el-form-item>
<el-form-item
:label="t('deliveryTemplateId')"
prop="delivery_template_id"
v-show="
formData.delivery_type.indexOf('express') != -1 &&
formData.is_free_shipping == 0 &&
formData.fee_type == 'template'
"
>
<el-select
v-model="formData.delivery_template_id"
:placeholder="t('deliveryTemplateIdPlaceholder')"
filterable
autocomplete="off"
clearable
>
<el-option
v-for="item in deliveryTemplateOptions"
:key="item.template_id"
:label="item.template_name"
:value="item.template_id"
/>
<el-form-item :label="t('deliveryTemplateId')" prop="delivery_template_id" v-show="formData.delivery_type.indexOf('express') != -1 && formData.is_free_shipping == 0 && formData.fee_type == 'template'">
<el-select v-model="formData.delivery_template_id" :placeholder="t('deliveryTemplateIdPlaceholder')" filterable autocomplete="off" clearable>
<el-option v-for="item in deliveryTemplateOptions" :key="item.template_id" :label="item.template_name" :value="item.template_id" />
</el-select>
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshDeliveryTemplate"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toDeliveryTemplateEvent"
>{{ t('addDeliveryTemplateId') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshDeliveryTemplate">{{ t('refresh') }}</span>
<span class="cursor-pointer text-primary" @click="toDeliveryTemplateEvent">{{ t('addDeliveryTemplateId') }}</span>
</div>
</el-form-item>
</div>
@ -314,48 +125,18 @@
</el-radio-group>
</el-form-item>
<el-form-item :label="t('stockNum')" prop="">
<el-input
v-model.trim="formData.stock"
clearable
placeholder="0"
class="input-width-short"
show-word-limit
maxlength="8"
@keyup="filterNumber($event)"
@blur="formData.stock = $event.target.value"
/>
<el-input v-model.trim="formData.stock" clearable placeholder="0" class="input-width-short" show-word-limit maxlength="8" @keyup="filterNumber($event)" @blur="formData.stock = $event.target.value" />
</el-form-item>
<div
class="mt-[10px] ml-[120px] text-[12px] text-[#999] leading-[20px]"
>
{{ t('stockNumTips') }}
</div>
<div class="mt-[10px] ml-[120px] text-[12px] text-[#999] leading-[20px]">{{ t('stockNumTips') }}</div>
</div>
<!-- 万能表单 -->
<el-form-item v-if="activeMenu === 'diy_form'" :label="t('diyForm')">
<el-select
v-model="formData.form_id"
:placeholder="t('diyFormPlaceholder')"
clearable
>
<el-option
v-for="item in diyFormOptions"
:key="item.form_id"
:label="item.page_title"
:value="item.form_id"
/>
<el-select v-model="formData.form_id" :placeholder="t('diyFormPlaceholder')" clearable>
<el-option v-for="item in diyFormOptions" :key="item.form_id" :label="item.page_title" :value="item.form_id" />
</el-select>
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshDiyForm(true)"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toDiyFormEvent"
>{{ t('addDiyForm') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshDiyForm(true)">{{ t('refresh') }}</span>
<span class="cursor-pointer text-primary" @click="toDiyFormEvent">{{ t('addDiyForm') }}</span>
</div>
</el-form-item>
</el-col>
@ -365,12 +146,7 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -388,14 +164,11 @@ import {
getServeList,
getCategoryTree,
getGoodsBatchSetDict,
goodsBatchSet,
goodsBatchSet
} from '@/addon/shop/api/goods'
import { getPosterList } from '@/app/api/poster'
import { getDiyFormList } from '@/app/api/diy_form'
import {
getShopDeliveryList,
getShippingTemplateList,
} from '@/addon/shop/api/delivery'
import {getShopDeliveryList,getShippingTemplateList} from '@/addon/shop/api/delivery'
const emit = defineEmits(['load'])
const showDialog = ref(false)
@ -422,7 +195,7 @@ const initialFormData = {
stock_type: 'inc',
is_free_shipping: 1,
is_gift: 0,
stock: '',
stock: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -431,7 +204,7 @@ const regExp: any = {
required: /[\S]+/,
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
special: /^\d{0,10}(.?\d{0,3})$/,
special: /^\d{0,10}(.?\d{0,3})$/
}
const formRef = ref<FormInstance>()
const formRules = reactive({
@ -450,29 +223,21 @@ const formRules = reactive({
} else {
callback()
}
},
},
}
}
],
delivery_type: [
{ required: true, message: t('deliveryTypePlaceholder'), trigger: 'blur' },
{ required: true, message: t('deliveryTypePlaceholder'), trigger: 'blur' }
],
goods_category: [
{
required: true,
message: t('goodsCategoryPlaceholderTwo'),
trigger: 'blur',
},
{ required: true, message: t('goodsCategoryPlaceholderTwo'), trigger: 'blur' }
],
delivery_money: [
{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (activeMenu.value == 'delivery') {
if (
formData.delivery_type.indexOf('express') != -1 &&
formData.is_free_shipping == 0 &&
formData.fee_type == 'fixed'
) {
if (formData.delivery_type.indexOf('express') != -1 && formData.is_free_shipping == 0 && formData.fee_type == 'fixed') {
if (formData.delivery_template_id.length == 0 && value === '') {
callback(new Error(t('deliveryMoneyPlaceholder')))
} else if (isNaN(value) || !regExp.digit.test(value)) {
@ -488,19 +253,15 @@ const formRules = reactive({
} else {
callback()
}
},
},
}
}
],
delivery_template_id: [
{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (activeMenu.value == 'delivery') {
if (
formData.delivery_type.indexOf('express') != -1 &&
formData.is_free_shipping == 0 &&
formData.fee_type == 'template'
) {
if (formData.delivery_type.indexOf('express') != -1 && formData.is_free_shipping == 0 && formData.fee_type == 'template') {
if (formData.delivery_money.length == 0 && value === '') {
callback(new Error(t('deliveryTemplateIdPlaceholder')))
} else {
@ -512,16 +273,16 @@ const formRules = reactive({
} else {
callback()
}
},
},
],
}
}
]
})
const goods_ids: any = []
const goods_ids: any = [];
const show = (multipleSelection: any) => {
multipleSelection.forEach((item: any) => {
goods_ids.push(item.goods_id)
})
goods_ids.push(item.goods_id);
});
showDialog.value = true
}
@ -549,14 +310,13 @@ const confirm = async (formEl: FormInstance | undefined) => {
let data = {
goods_ids: goods_ids,
set_type: activeMenu.value,
set_value: formData,
set_value: formData
}
goodsBatchSet(data)
.then((res) => {
goodsBatchSet(data).then((res) => {
if (['stock'].indexOf(activeMenu.value) != -1) {
activeMenu.value = 'label'
activeMenu.value = 'label';
showDialog.value = false
goods_ids.splice(0, goods_ids.length)
goods_ids.splice(0, goods_ids.length);
Object.assign(formData, {
label_ids: [],
service_ids: [],
@ -571,12 +331,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
delivery_type: [],
is_free_shipping: 1,
is_gift: 0,
})
});
emit('load')
}
loading.value = false
})
.catch((err) => {
}).catch(err => {
loading.value = false
})
}
@ -590,7 +349,7 @@ const handleMenuSelect = (index: string) => {
const setTypeList = reactive([])
const getGoodsTypeList = () => {
getGoodsBatchSetDict().then((res) => {
Object.assign(setTypeList, res.data)
Object.assign(setTypeList, res.data);
})
}
getGoodsTypeList()
@ -601,7 +360,7 @@ const labelOptions = reactive([])
//
const toGoodsLabelEvent = () => {
const url = router.resolve({
path: '/shop/goods/label',
path: '/shop/goods/label'
})
window.open(url.href)
}
@ -615,7 +374,7 @@ const refreshGoodsLabel = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
type: 'success'
})
}
}
@ -630,7 +389,7 @@ const serviceOptions = reactive([])
//
const toGoodsServiceEvent = () => {
const url = router.resolve({
path: '/shop/goods/service',
path: '/shop/goods/service'
})
window.open(url.href)
}
@ -644,7 +403,7 @@ const refreshGoodsService = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
type: 'success'
})
}
}
@ -658,7 +417,7 @@ refreshGoodsService()
//
const goodsCategoryOptions = reactive([])
const goodsCategoryProps = {
multiple: true,
multiple: true
}
const categoryHandleChange = (value: any) => {
// console.log(value, goodsEdit.formData.goods_category, goodsEdit.formData.goods_category.toString())
@ -667,7 +426,7 @@ const categoryHandleChange = (value: any) => {
//
const toGoodsCategoryEvent = () => {
const url = router.resolve({
path: '/shop/goods/category',
path: '/shop/goods/category'
})
window.open(url.href)
}
@ -684,25 +443,21 @@ const refreshGoodsCategory = (bool = false) => {
item.child_list.forEach((childItem: any) => {
children.push({
value: childItem.category_id,
label: childItem.category_name,
label: childItem.category_name
})
})
}
goodsCategoryTree.push({
value: item.category_id,
label: item.category_name,
children,
children
})
})
goodsCategoryOptions.splice(
0,
goodsCategoryOptions.length,
...goodsCategoryTree
)
goodsCategoryOptions.splice(0, goodsCategoryOptions.length, ...goodsCategoryTree)
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
type: 'success'
})
}
}
@ -719,7 +474,7 @@ const brandOptions = reactive([])
//
const toGoodsBrandEvent = () => {
const url = router.resolve({
path: '/shop/goods/brand',
path: '/shop/goods/brand'
})
window.open(url.href)
}
@ -733,7 +488,7 @@ const refreshGoodsBrand = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
type: 'success'
})
}
}
@ -749,7 +504,7 @@ const posterOptions = reactive([])
//
const toPosterEvent = () => {
const url = router.resolve({
path: '/poster/list',
path: '/poster/list'
})
window.open(url.href)
}
@ -757,7 +512,7 @@ const toPosterEvent = () => {
//
const refreshGoodsPoster = (bool = false) => {
getPosterList({
type: 'shop_goods',
type: 'shop_goods'
}).then((res) => {
const data = res.data
if (data) {
@ -765,7 +520,7 @@ const refreshGoodsPoster = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
type: 'success'
})
}
}
@ -775,6 +530,7 @@ const refreshGoodsPoster = (bool = false) => {
refreshGoodsPoster()
/** *****************商品海报-end *************************/
//
const deliveryTypeCheckBox = reactive([])
@ -796,7 +552,7 @@ getShopDeliveryList().then((res) => {
//
const toDeliveryTemplateEvent = () => {
const url = router.resolve({
path: '/shop/order/shipping/template',
path: '/shop/order/shipping/template'
})
window.open(url.href)
}
@ -810,7 +566,7 @@ const refreshDeliveryTemplate = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
type: 'success'
})
}
}
@ -825,7 +581,7 @@ const diyFormOptions = reactive([])
//
const toDiyFormEvent = () => {
const url = router.resolve({
path: '/diy_form/list',
path: '/diy_form/list'
})
window.open(url.href)
}
@ -834,7 +590,7 @@ const toDiyFormEvent = () => {
const refreshDiyForm = (bool = false) => {
getDiyFormList({
type: 'DIY_FORM_GOODS_DETAIL',
status: 1,
status: 1
}).then((res) => {
const data = res.data
if (data) {
@ -842,7 +598,7 @@ const refreshDiyForm = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
type: 'success'
})
}
}
@ -854,7 +610,7 @@ refreshDiyForm()
defineExpose({
showDialog,
show,
show
})
</script>

34
admin/src/addon/shop/views/goods/components/goods-category-spread-popup.vue

@ -1,14 +1,8 @@
<template>
<el-dialog
v-model="showDialog"
:title="t('goodsCategorySpreadTitle')"
width="500px"
:destroy-on-close="true"
>
<el-dialog v-model="showDialog" :title="t('goodsCategorySpreadTitle')" width="500px" :destroy-on-close="true">
<div class="promote-flex flex">
<div
class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]"
>
<div class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]">
<el-image :src="wapImage" />
</div>
@ -16,16 +10,14 @@
<div class="mb-[10px]">{{ t('spreadLink') }}</div>
<el-input class="mb-[10px]" readonly :value="wapPreview">
<template #append>
<el-button @click="copyEvent(wapPreview)">{{
t('copy')
}}</el-button>
<el-button @click="copyEvent(wapPreview)">{{ t('copy') }}</el-button>
</template>
</el-input>
<a class="text-primary" :href="wapImage" download>{{
t('downloadQrcode')
}}</a>
<a class="text-primary" :href="wapImage" download>{{ t('downloadQrcode') }}</a>
</div>
</div>
</el-dialog>
</template>
@ -68,11 +60,7 @@ getUrl().then((res: any) => {
const loadQrcode = () => {
wapPreview.value = `${wapUrl.value}${page.value}`
// errorCorrectionLevelLH()
QRCode.toDataURL(wapPreview.value, {
errorCorrectionLevel: 'L',
margin: 0,
width: 120,
}).then((url) => {
QRCode.toDataURL(wapPreview.value, { errorCorrectionLevel: 'L', margin: 0, width: 120 }).then(url => {
wapImage.value = url
})
}
@ -90,7 +78,7 @@ const copyEvent = (text: string) => {
if (!isSupported.value) {
ElMessage({
message: t('notSupportCopy'),
type: 'warning',
type: 'warning'
})
}
copy(text)
@ -100,14 +88,14 @@ watch(copied, () => {
if (copied.value) {
ElMessage({
message: t('copySuccess'),
type: 'success',
type: 'success'
})
}
})
defineExpose({
showDialog,
show,
show
})
</script>

209
admin/src/addon/shop/views/goods/components/goods-member-price-popup.vue

@ -1,13 +1,7 @@
<template>
<div>
<el-dialog
v-model="showDialog"
:title="t('editMemberPricePopupTitle')"
width="1100px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-dialog v-model="showDialog" :title="t('editMemberPricePopupTitle')" width="1100px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-form :model="formData" label-width="120px" class="page-form">
<el-form-item :label="t('memberDiscount')">
<div>
@ -16,50 +10,21 @@
<el-radio label="discount">{{ t('discount') }}</el-radio>
<el-radio label="fixed_price">{{ t('fixedPrice') }}</el-radio>
</el-radio-group>
<div
class="text-[12px] text-[#999] leading-[20px]"
v-if="formData.member_discount == 'discount'"
>
{{ t('discountHint') }}
</div>
<div
class="text-[12px] text-[#999] leading-[20px]"
v-if="formData.member_discount == 'fixed_price'"
>
{{ t('fixedPriceHint') }}
</div>
<div class="text-[12px] text-[#999] leading-[20px]" v-if="formData.member_discount == 'discount'">{{t('discountHint')}}</div>
<div class="text-[12px] text-[#999] leading-[20px]" v-if="formData.member_discount == 'fixed_price'">{{t('fixedPriceHint')}}</div>
</div>
</el-form-item>
<el-form-item
v-if="
formData.member_discount == 'discount' && memberDiscountLevel.length
"
>
<el-table
:data="[{}]"
size="large"
v-loading="tableData.loading"
max-height="450"
@selection-change="handleSelectionChange"
>
<el-form-item v-if="formData.member_discount == 'discount' && memberDiscountLevel.length">
<el-table :data="[{}]" size="large" v-loading="tableData.loading" max-height="450" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
fixed
prop="sku_name"
:label="t('memberLevel')"
width="150"
>
<el-table-column fixed prop="sku_name" :label="t('memberLevel')" width="150" >
<template #default>
{{t('memberEnjoyDiscount')}}
</template>
</el-table-column>
<el-table-column
v-for="(item, index) in memberDiscountLevel"
:key="index"
:label="item.level_name"
>
<el-table-column v-for="(item,index) in memberDiscountLevel" :key="index" :label="item.level_name">
<template #default="{ row }">
{{item.level_benefits.discount.discount}}
</template>
@ -68,60 +33,21 @@
</el-form-item>
<el-form-item v-if="formData.member_discount == 'fixed_price'">
<div class="mb-[10px] flex items-center">
<el-checkbox
v-model="toggleCheckbox"
@change="toggleChange"
size="large"
class="px-[14px]"
:indeterminate="isIndeterminate"
/>
<el-button
v-for="(item, index) in tableData.member_level"
:key="index"
size="small"
@click="batchGoodsBtn(item.level_id)"
>
<el-checkbox v-model="toggleCheckbox" @change="toggleChange" size="large" class="px-[14px]" :indeterminate="isIndeterminate" />
<el-button v-for="(item,index) in tableData.member_level" :key="index" size="small" @click="batchGoodsBtn(item.level_id)">
{{item.level_name}}
</el-button>
</div>
<el-table
:data="tableData.data"
size="large"
v-loading="tableData.loading"
max-height="450"
@selection-change="handleSelectionChange"
ref="goodsListTableRef"
>
<el-table :data="tableData.data" size="large" v-loading="tableData.loading" max-height="450" @selection-change="handleSelectionChange" ref="goodsListTableRef">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
fixed
prop="sku_name"
:label="t('goodsSku')"
width="150"
/>
<el-table-column
fixed
prop="price"
:label="t('skuPrice')"
width="120"
/>
<el-table-column
v-for="(item, index) in tableData.member_level"
:key="index"
:label="item.level_name"
width="190"
>
<el-table-column fixed prop="sku_name" :label="t('goodsSku')" width="150" />
<el-table-column fixed prop="price" :label="t('skuPrice')" width="120" />
<el-table-column v-for="(item,index) in tableData.member_level" :key="index" :label="item.level_name" width="190">
<template #default="{ row }">
<el-input
v-model.trim="row[`level_${item.level_id}`]"
maxlength="8"
clearable
class="w-full"
@keyup="filterDigit($event)"
>
<el-input v-model.trim="row[`level_${item.level_id}`]" maxlength="8" clearable class="w-full" @keyup="filterDigit($event)">
<template #append>{{t('yuanUnit')}}</template>
</el-input>
</template>
@ -130,34 +56,17 @@
</el-form-item>
</el-form>
<el-dialog
v-model="memberPriceDialog"
:title="t('editMemberPrice')"
width="400px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-dialog v-model="memberPriceDialog" :title="t('editMemberPrice')" width="400px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-form :model="formData" label-width="80px" class="page-form">
<el-form-item :label="t('memberPrice')" prop="member_price">
<el-input
v-model.trim="memberPrice"
:placeholder="t('memberPricePlaceholder')"
maxlength="8"
clearable
@keyup="filterDigit($event)"
/>
<el-input v-model.trim="memberPrice" :placeholder="t('memberPricePlaceholder')" maxlength="8" clearable @keyup="filterDigit($event)" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="memberPriceDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="memberPriceSave">{{
t('confirm')
}}</el-button>
<el-button @click="memberPriceDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="memberPriceSave">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -180,12 +89,12 @@ import { ElMessage } from 'element-plus'
import {
getGoodsSkuList,
editGoodsListMemberPrice,
editGoodsListMemberPrice
} from '@/addon/shop/api/goods'
const goodsListTableRef = ref()
const formData: any = reactive({
member_discount: '',
member_discount: ''
})
const goods: any = reactive({})
const showDialog = ref(false)
@ -196,7 +105,7 @@ const emit = defineEmits(['load'])
const tableData = reactive({
loading: true,
data: [],
member_level: [],
member_level: []
})
/**
* 获取商品列表
@ -205,9 +114,8 @@ const loadGoodsList = () => {
tableData.loading = true
getGoodsSkuList({
goods_id: goods.goods_id,
})
.then((res) => {
goods_id: goods.goods_id
}).then(res => {
tableData.loading = false
tableData.data = res.data
tableData.data.forEach((item: any, index, Array: any) => {
@ -218,25 +126,18 @@ const loadGoodsList = () => {
//
tableData.member_level.forEach((levelItem: any, levelIndex) => {
if (!item.member_price) {
Array[index][`level_${levelItem.level_id}`] = parseFloat(
item.price
).toFixed(2)
Array[index][`level_${levelItem.level_id}`] = parseFloat(item.price).toFixed(2)
} else if (item.member_price) {
const memberPrice = JSON.parse(item.member_price)
if (memberPrice[`level_${levelItem.level_id}`]) {
Array[index][`level_${levelItem.level_id}`] = parseFloat(
memberPrice[`level_${levelItem.level_id}`]
).toFixed(2)
Array[index][`level_${levelItem.level_id}`] = parseFloat(memberPrice[`level_${levelItem.level_id}`]).toFixed(2)
} else {
Array[index][`level_${levelItem.level_id}`] = parseFloat(
item.price
).toFixed(2)
Array[index][`level_${levelItem.level_id}`] = parseFloat(item.price).toFixed(2)
}
}
})
})
})
.catch(() => {
}).catch(() => {
tableData.loading = false
})
}
@ -254,23 +155,14 @@ const show = (data: any, levelData: any) => {
if (!item.level_benefits || item.level_benefits == null) {
Array[index].level_benefits = {
discount: {
discount: '原价',
},
discount: '原价'
}
} else if (
item.level_benefits &&
item.level_benefits != null &&
!item.level_benefits.discount
) {
}
} else if (item.level_benefits && item.level_benefits != null && !item.level_benefits.discount) {
Array[index].level_benefits.discount = {
discount: '原价',
discount: '原价'
}
} else if (
item.level_benefits &&
item.level_benefits != null &&
item.level_benefits.discount &&
!item.level_benefits.discount.discount
) {
} else if (item.level_benefits && item.level_benefits != null && item.level_benefits.discount && !item.level_benefits.discount.discount) {
Array[index].level_benefits.discount.discount = '原价'
} else {
Array[index].level_benefits.discount.discount += '折'
@ -298,10 +190,7 @@ const handleSelectionChange = (val: []) => {
multipleSelection.value = deepClone(val)
toggleCheckbox.value = false
if (
multipleSelection.value.length > 0 &&
multipleSelection.value.length < tableData.data.length
) {
if (multipleSelection.value.length > 0 && multipleSelection.value.length < tableData.data.length) {
isIndeterminate.value = true
} else {
isIndeterminate.value = false
@ -318,7 +207,7 @@ const batchGoodsBtn = (level_id: any) => {
if (!multipleSelection.value.length) {
ElMessage({
message: '请选择要操作的商品',
type: 'warning',
type: 'warning'
})
return false
}
@ -336,11 +225,7 @@ const memberPriceSave = () => {
const idArr = multipleSelection.value.map((obj: any) => obj.sku_id)
tableData.data.forEach((item: any, index, Array: any) => {
if (idArr.indexOf(item.sku_id) > -1) {
Array[index][`level_${currLevelId.value}`] = parseFloat(
memberPrice.value
).toFixed(2)
}
if (idArr.indexOf(item.sku_id) > -1) { Array[index][`level_${currLevelId.value}`] = parseFloat(memberPrice.value).toFixed(2) }
})
memberPrice.value = ''
@ -358,22 +243,14 @@ const save = () => {
obj.member_price = {}
tableData.member_level.forEach((levelItem: any, levelIndex) => {
if (verify) {
obj.member_price[`level_${levelItem.level_id}`] =
item[`level_${levelItem.level_id}`]
obj.member_price[`level_${levelItem.level_id}`] = item[`level_${levelItem.level_id}`]
if (parseFloat(item[`level_${levelItem.level_id}`]) <= 0) {
verify = false
ElMessage.error(
`[${item.sku_name}][${levelItem.level_name}]的指定价格不能小于等于零`
)
ElMessage.error(`[${item.sku_name}][${levelItem.level_name}]的指定价格不能小于等于零`)
}
if (
parseFloat(item[`level_${levelItem.level_id}`]) >
parseFloat(item.price)
) {
if (parseFloat(item[`level_${levelItem.level_id}`]) > parseFloat(item.price)) {
verify = false
ElMessage.error(
`[${item.sku_name}][${levelItem.level_name}]的指定价格不能大于商品原价`
)
ElMessage.error(`[${item.sku_name}][${levelItem.level_name}]的指定价格不能大于商品原价`)
}
}
})
@ -389,8 +266,8 @@ const save = () => {
editGoodsListMemberPrice({
goods_id: goods.goods_id,
member_discount: formData.member_discount,
sku_list,
}).then((res) => {
sku_list
}).then(res => {
saveLoad = false
emit('load')
showDialog.value = false
@ -399,7 +276,7 @@ const save = () => {
defineExpose({
showDialog,
show,
show
})
</script>

168
admin/src/addon/shop/views/goods/components/goods-price-edit-popup.vue

@ -1,44 +1,21 @@
<template>
<div>
<el-dialog
v-model="showDialog"
:title="t('editPricePopupTitle')"
width="800px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-dialog v-model="showDialog" :title="t('editPricePopupTitle')" width="800px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<div class="flex items-center mb-[10px]">
<div class="min-w-[70px] h-[70px] flex items-center justify-center">
<el-image
v-if="goods.goods_cover_thumb_small"
class="w-[70px] h-[70px]"
:src="img(goods.goods_cover_thumb_small)"
fit="contain"
>
<el-image v-if="goods.goods_cover_thumb_small" class="w-[70px] h-[70px]" :src="img(goods.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="goods.goods_name" class="multi-hidden">{{
goods.goods_name
}}</span>
<span class="text-primary text-[12px]">{{
goods.goods_type_name
}}</span>
<span :title="goods.goods_name" class="multi-hidden">{{ goods.goods_name }}</span>
<span class="text-primary text-[12px]">{{ goods.goods_type_name }}</span>
</div>
</div>
@ -47,96 +24,41 @@
<label>{{ t('batchOperationSku') }}</label>
<div v-if="batchOperation.field">
<el-input
v-model.trim="batchOperation.value"
clearable
:placeholder="t(batchOperation.field)"
class="set-input"
maxlength="8"
:autofocus="true"
/>
<el-button type="primary" @click="saveBatch">{{
t('confirm')
}}</el-button>
<el-input v-model.trim="batchOperation.value" clearable :placeholder="t(batchOperation.field)" class="set-input" maxlength="8" :autofocus="true" />
<el-button type="primary" @click="saveBatch">{{ t('confirm') }}</el-button>
<el-button @click="clearBatch">{{ t('cancel') }}</el-button>
</div>
<div v-else>
<el-button
type="primary"
link
@click="setBatchField('price')"
v-if="activeGoodsCount == 0"
>{{ t('price') }}</el-button
>
<el-button
type="primary"
link
@click="setBatchField('market_price')"
>{{ t('marketPrice') }}</el-button
>
<el-button type="primary" link @click="setBatchField('cost_price')">{{
t('costPrice')
}}</el-button>
<el-button type="primary" link @click="setBatchField('price')" v-if="activeGoodsCount == 0">{{ t('price') }}</el-button>
<el-button type="primary" link @click="setBatchField('market_price')">{{ t('marketPrice') }}</el-button>
<el-button type="primary" link @click="setBatchField('cost_price')">{{ t('costPrice') }}</el-button>
</div>
</div>
<el-table
:data="goodsTable.data"
size="large"
v-loading="goodsTable.loading"
max-height="400"
>
<el-table :data="goodsTable.data" size="large" v-loading="goodsTable.loading" max-height="400">
<template #empty>
<span>{{ !goodsTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="sku_name"
:label="t('skuName')"
min-width="120"
v-if="goodsTable.data.length > 1"
/>
<el-table-column prop="sku_name" :label="t('skuName')" min-width="120" v-if="goodsTable.data.length > 1" />
<el-table-column prop="price" :label="t('price')" min-width="120">
<template #default="{ row }">
<el-input
v-model.trim="row.price"
clearable
placeholder="0.00"
maxlength="8"
:disabled="activeGoodsCount > 0"
/>
<el-input v-model.trim="row.price" clearable placeholder="0.00" maxlength="8" :disabled="activeGoodsCount > 0" />
</template>
</el-table-column>
<el-table-column
prop="market_price"
:label="t('marketPrice')"
min-width="120"
>
<el-table-column prop="market_price" :label="t('marketPrice')" min-width="120">
<template #default="{ row }">
<el-input
v-model.trim="row.market_price"
clearable
placeholder="0.00"
maxlength="8"
/>
<el-input v-model.trim="row.market_price" clearable placeholder="0.00" maxlength="8" />
</template>
</el-table-column>
<el-table-column
prop="cost_price"
:label="t('costPrice')"
min-width="120"
>
<el-table-column prop="cost_price" :label="t('costPrice')" min-width="120">
<template #default="{ row }">
<el-input
v-model.trim="row.cost_price"
clearable
placeholder="0.00"
maxlength="8"
/>
<el-input v-model.trim="row.cost_price" clearable placeholder="0.00" maxlength="8" />
</template>
</el-table-column>
</el-table>
<template #footer>
@ -158,7 +80,7 @@ import { ElMessage } from 'element-plus'
import {
getActiveGoodsCount,
getGoodsSkuList,
editGoodsListPrice,
editGoodsListPrice
} from '@/addon/shop/api/goods'
const goods: any = reactive({})
@ -170,12 +92,12 @@ const emit = defineEmits(['load'])
const goodsTable = reactive({
loading: true,
data: [],
data: []
})
const batchOperation: any = reactive({
field: '', //
value: '', //
value: '' //
})
const setBatchField = (value: string, regexp: string) => {
@ -192,7 +114,7 @@ const saveBatch = () => {
if (batchOperation.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t(batchOperation.field + 'Placeholder')}`,
message: `${t(batchOperation.field + 'Placeholder')}`
})
return
}
@ -200,7 +122,7 @@ const saveBatch = () => {
if (!regExp.digit.test(batchOperation.value)) {
ElMessage({
type: 'warning',
message: `${t(batchOperation.field + 'Tips')}`,
message: `${t(batchOperation.field + 'Tips')}`
})
return
}
@ -208,7 +130,7 @@ const saveBatch = () => {
if (batchOperation.value < 0) {
ElMessage({
type: 'warning',
message: `${t(batchOperation.field + 'NotZeroTips')}`,
message: `${t(batchOperation.field + 'NotZeroTips')}`
})
return
}
@ -227,35 +149,33 @@ const loadGoodsList = () => {
goodsTable.loading = true
getGoodsSkuList({
goods_id: goods.goods_id,
})
.then((res) => {
goods_id: goods.goods_id
}).then(res => {
goodsTable.loading = false
goodsTable.data = res.data
})
.catch(() => {
}).catch(() => {
goodsTable.loading = false
})
}
const show = (data: any) => {
Object.assign(goods, data)
getActiveGoodsCountFn()
getActiveGoodsCountFn();
loadGoodsList()
showDialog.value = true
}
const getActiveGoodsCountFn = ()=>{
getActiveGoodsCount({
goods_id: goods.goods_id,
goods_id: goods.goods_id
}).then((res)=>{
activeGoodsCount.value = res.data
activeGoodsCount.value = res.data;
})
}
//
const regExp: any = {
digit: /^\d{0,10}(.?\d{0,2})$/,
digit: /^\d{0,10}(.?\d{0,2})$/
}
const verify = () => {
@ -267,14 +187,14 @@ const verify = () => {
result = false
ElMessage({
type: 'warning',
message: `${t('priceTips')}`,
message: `${t('priceTips')}`
})
break
} else if (item.price < 0) {
result = false
ElMessage({
type: 'warning',
message: `${t('priceNotZeroTips')}`,
message: `${t('priceNotZeroTips')}`
})
break
}
@ -283,14 +203,14 @@ const verify = () => {
result = false
ElMessage({
type: 'warning',
message: `${t('marketPriceTips')}`,
message: `${t('marketPriceTips')}`
})
break
} else if (item.market_price < 0) {
result = false
ElMessage({
type: 'warning',
message: `${t('marketPriceNotZeroTips')}`,
message: `${t('marketPriceNotZeroTips')}`
})
break
}
@ -299,14 +219,14 @@ const verify = () => {
result = false
ElMessage({
type: 'warning',
message: `${t('costPriceTips')}`,
message: `${t('costPriceTips')}`
})
break
} else if (item.cost_price < 0) {
result = false
ElMessage({
type: 'warning',
message: `${t('costPriceNotZeroTips')}`,
message: `${t('costPriceNotZeroTips')}`
})
break
}
@ -323,14 +243,14 @@ const save = () => {
sku_id: item.sku_id,
price: item.price,
market_price: item.market_price,
cost_price: item.cost_price,
cost_price: item.cost_price
})
})
editGoodsListPrice({
goods_id: goods.goods_id,
sku_list,
}).then((res) => {
emit('load')
sku_list
}).then(res => {
emit('load');
showDialog.value = false
})
}
@ -338,7 +258,7 @@ const save = () => {
defineExpose({
showDialog,
show,
show
})
</script>

578
admin/src/addon/shop/views/goods/components/goods-select-popup.vue

@ -3,90 +3,38 @@
<div @click="show">
<slot>
<el-button>{{ t('goodsSelectPopupSelectGoodsButton') }}</el-button>
<div
class="inline-block ml-[10px] text-[14px]"
v-show="goodsIds.length"
>
<div class="inline-block ml-[10px] text-[14px]" v-show="goodsIds.length">
<span>{{ t('goodsSelectPopupSelect') }}</span>
<span class="text-primary mx-[2px]">{{ goodsIds.length }}</span>
<span>{{ t('goodsSelectPopupPiece') }}</span>
</div>
</slot>
</div>
<el-dialog
v-model="showDialog"
:title="t('goodsSelectPopupSelectGoodsDialog')"
width="1000px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-form
:inline="true"
:model="goodsTable.searchParam"
ref="searchFormRef"
>
<el-dialog v-model="showDialog" :title="t('goodsSelectPopupSelectGoodsDialog')" width="1000px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-form :inline="true" :model="goodsTable.searchParam" ref="searchFormRef">
<el-form-item prop="select_type" class="form-item-wrap">
<el-select
v-model="goodsTable.searchParam.select_type"
@change="handleSelectTypeChange"
>
<el-select v-model="goodsTable.searchParam.select_type" @change="handleSelectTypeChange">
<el-option :label="t('goodsSelectPopupAllGoods')" value="all" />
<el-option
:label="t('goodsSelectPopupSelectedGoods')"
value="selected"
/>
<el-option :label="t('goodsSelectPopupSelectedGoods')" value="selected" />
</el-select>
</el-form-item>
<el-form-item
:label="t('goodsSelectPopupGoodsName')"
prop="keyword"
class="form-item-wrap"
>
<el-input
v-model.trim="goodsTable.searchParam.keyword"
:placeholder="t('goodsSelectPopupGoodsNamePlaceholder')"
maxlength="60"
/>
<el-form-item :label="t('goodsSelectPopupGoodsName')" prop="keyword" class="form-item-wrap">
<el-input v-model.trim="goodsTable.searchParam.keyword" :placeholder="t('goodsSelectPopupGoodsNamePlaceholder')" maxlength="60" />
</el-form-item>
<el-form-item
:label="t('goodsSelectPopupGoodsCategory')"
prop="goods_category"
class="form-item-wrap"
>
<el-cascader
v-model="goodsTable.searchParam.goods_category"
:options="goodsCategoryOptions"
:placeholder="t('goodsSelectPopupGoodsCategoryPlaceholder')"
clearable
:props="{ value: 'value', label: 'label', emitPath: false }"
/>
<el-form-item :label="t('goodsSelectPopupGoodsCategory')" prop="goods_category" class="form-item-wrap">
<el-cascader v-model="goodsTable.searchParam.goods_category"
:options="goodsCategoryOptions" :placeholder="t('goodsSelectPopupGoodsCategoryPlaceholder')"
clearable :props="{ value: 'value', label: 'label', emitPath:false }" />
</el-form-item>
<el-form-item
:label="t('goodsSelectPopupGoodsType')"
prop="goods_type"
class="form-item-wrap"
>
<el-select
v-model="goodsTable.searchParam.goods_type"
:placeholder="t('goodsSelectPopupGoodsTypePlaceholder')"
clearable
>
<el-option
v-for="item in goodsType"
:key="item.type"
:label="item.name"
:value="item.type"
/>
<el-form-item :label="t('goodsSelectPopupGoodsType')" prop="goods_type" class="form-item-wrap">
<el-select v-model="goodsTable.searchParam.goods_type" :placeholder="t('goodsSelectPopupGoodsTypePlaceholder')" clearable>
<el-option v-for="item in goodsType" :key="item.type" :label="item.name" :value="item.type" />
</el-select>
</el-form-item>
<el-form-item class="form-item-wrap">
<el-button type="primary" @click="loadGoodsList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadGoodsList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
@ -94,87 +42,37 @@
<div class="table-head flex items-center bg-[#f5f7f9] py-[8px]">
<div class="w-[3%]"></div>
<div class="w-[7%]">
<el-checkbox
v-model="staircheckAll"
:indeterminate="isStairIndeterminate"
@change="handleCheckAllChange"
/>
<el-checkbox v-model="staircheckAll" :indeterminate="isStairIndeterminate" @change="handleCheckAllChange" />
</div>
<div class="w-[50%]">商品信息</div>
<div class="w-[20%]">商品价格</div>
<div class="w-[20%]">库存</div>
</div>
<div class="table-body h-[350px] overflow-y-auto">
<div
v-for="(row, rowIndex) in goodsTable.data"
:key="rowIndex"
class="flex flex-col"
>
<div v-for="(row,rowIndex) in goodsTable.data" :key="rowIndex" class="flex flex-col">
<!-- 内容 -->
<div
class="flex items-center border-solid border-[#e5e7eb] py-[8px] border-b-[1px]"
>
<div class="flex items-center border-solid border-[#e5e7eb] py-[8px] border-b-[1px]">
<div v-if="prop.mode == 'spu'" class="w-[3%]"></div>
<div
v-if="prop.mode == 'sku' && row.skuList.length > 1"
class="w-[3%] cursor-pointer text-center !text-[10px]"
@click="secondLevelArrowChange(row)"
:class="{
'iconfont iconxiangyoujiantou': row.skuList.length,
'arrow-show': row.isShow,
}"
></div>
<div
v-if="prop.mode == 'sku' && row.skuList.length <= 1"
class="w-[3%]"
></div>
<div v-if="prop.mode == 'sku' && row.skuList.length > 1" class="w-[3%] cursor-pointer text-center !text-[10px]" @click="secondLevelArrowChange(row)" :class="{'iconfont iconxiangyoujiantou': row.skuList.length, 'arrow-show': row.isShow}"></div>
<div v-if="prop.mode == 'sku' && row.skuList.length <= 1" class="w-[3%]"></div>
<div class="w-[7%]">
<el-checkbox
v-model="row.secondLevelCheckAll"
:indeterminate="row.isSecondLevelIndeterminate"
@change="secondLevelHandleCheckAllChange($event, row)"
/>
<el-checkbox v-model="row.secondLevelCheckAll" :indeterminate="row.isSecondLevelIndeterminate" @change="secondLevelHandleCheckAllChange($event,row)" />
</div>
<div class="flex items-center cursor-pointer w-[50%]">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.goods_cover_thumb_small"
class="w-[60px] h-[60px]"
:src="img(row.goods_cover_thumb_small)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.goods_cover_thumb_small" class="w-[60px] h-[60px]" :src="img(row.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2 flex flex-col items-start">
<span
:title="row.goods_name"
class="multi-hidden leading-[1.4]"
>{{ row.goods_name }}</span
>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
<span
class="px-[4px] text-[12px] text-[#fff] rounded-[4px] bg-primary leading-[18px]"
v-if="row.is_gift == 1"
>赠品</span
>
<span :title="row.goods_name" class="multi-hidden leading-[1.4]">{{ row.goods_name }}</span>
<span class="text-primary text-[12px]">{{ row.goods_type_name }}</span>
<span class="px-[4px] text-[12px] text-[#fff] rounded-[4px] bg-primary leading-[18px]" v-if="row.is_gift == 1">赠品</span>
</div>
</div>
<div class="w-[20%]">{{ row.goodsSku.price }}</div>
@ -183,57 +81,25 @@
<div v-show="prop.mode == 'sku' && row.skuList.length > 1">
<!-- 子级 -->
<div
v-for="(item, index) in row.skuList"
:key="index"
class="flex items-center py-[8px] border-solid border-transparent border-b-[1px]"
:class="{
hidden: !row.isShow,
'border-[#e5e7eb]': index == row.skuList.length - 1,
}"
>
<div v-for="(item,index) in row.skuList" :key="index" class="flex items-center py-[8px] border-solid border-transparent border-b-[1px]" :class="{'hidden': !row.isShow, 'border-[#e5e7eb]': index == (row.skuList.length-1)}">
<div class="w-[6%]"></div>
<div class="w-[4%]">
<el-checkbox
v-model="item.threeLevelCheckAll"
@change="subChildHandleCheckAllChange($event, row, item)"
/>
<el-checkbox v-model="item.threeLevelCheckAll" @change="subChildHandleCheckAllChange($event,row,item)" />
</div>
<div class="flex items-center cursor-pointer w-[50%]">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="item.sku_image"
class="w-[60px] h-[60px]"
:src="img(item.sku_image)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="item.sku_image" class="w-[60px] h-[60px]" :src="img(item.sku_image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span
:title="item.sku_name || row.goods_name"
class="multi-hidden leading-[1.4]"
>{{ item.sku_name || row.goods_name }}</span
>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
<span :title="item.sku_name || row.goods_name" class="multi-hidden leading-[1.4]">{{ item.sku_name || row.goods_name }}</span>
<span class="text-primary text-[12px]">{{ row.goods_type_name }}</span>
</div>
</div>
<div class="w-[20%] flex">{{ item.price }}</div>
@ -242,10 +108,7 @@
</div>
</div>
<div
v-if="!goodsTable.data.length && !goodsTable.loading"
class="h-[60px] flex items-center justify-center border-solid border-[#e5e7eb] py-[12px] border-b-[1px]"
>
<div v-if="!goodsTable.data.length && !goodsTable.loading" class="h-[60px] flex items-center justify-center border-solid border-[#e5e7eb] py-[12px] border-b-[1px]">
暂无数据
</div>
</div>
@ -253,30 +116,16 @@
<div class="mt-[16px] flex">
<div class="flex items-center flex-1">
<div
class="layui-table-bottom-left-container mr-[10px]"
v-show="selectGoodsNum"
>
<div class="layui-table-bottom-left-container mr-[10px]" v-show="selectGoodsNum">
<span>{{ t('goodsSelectPopupBeforeTip') }}</span>
<span class="text-primary mx-[2px]">{{ selectGoodsNum }}</span>
<span>{{ t('goodsSelectPopupAfterTip') }}</span>
</div>
<el-button
type="primary"
link
@click="clear"
v-show="selectGoodsNum"
>{{ t('goodsSelectPopupClearGoods') }}</el-button
>
<el-button type="primary" link @click="clear" v-show="selectGoodsNum">{{ t('goodsSelectPopupClearGoods') }}</el-button>
</div>
<el-pagination
v-model:current-page="goodsTable.page"
v-model:page-size="goodsTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="goodsTable.total"
@size-change="loadGoodsList()"
@current-change="loadGoodsList"
/>
<el-pagination v-model:current-page="goodsTable.page" v-model:page-size="goodsTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="goodsTable.total"
@size-change="loadGoodsList()" @current-change="loadGoodsList" />
</div>
<template #footer>
@ -295,47 +144,42 @@ import { ref, reactive, computed, nextTick } from 'vue'
import { cloneDeep } from 'lodash-es'
import { img,deepClone } from '@/utils/common'
import { ElMessage } from 'element-plus'
import {
getGoodsSelectPageList,
getGoodsSkuNoPageList,
getCategoryTree,
getGoodsType,
} from '@/addon/shop/api/goods'
import { getGoodsSelectPageList,getGoodsSkuNoPageList, getCategoryTree, getGoodsType } from '@/addon/shop/api/goods'
const prop = defineProps({
modelValue: {
type: String,
default: '',
default: ''
},
max: {
type: Number,
default: 0,
default: 0
},
min: {
type: Number,
default: 0,
default: 0
},
mode: {
type: String,
default: 'spu', // spusku
default: 'spu' // spusku
},
way: {
type: String,
default: '', // single
default: '' // single
},
isGift: {
type: [String,Number],
default: 0, // 01
default: 0 // 01
},
})
const emit = defineEmits(['update:modelValue','goodsSelect'])
// prop.mode sku_goods_
let replacePrefix = prop.mode == 'sku' ? 'sku_' : 'goods_'
let replacePrefix = prop.mode == "sku" ? 'sku_' : 'goods_';
const isStairIndeterminate = ref(false)
const staircheckAll = ref(false)
const isStairIndeterminate = ref(false);
const staircheckAll = ref(false);
const goodsIds: any = computed({
get () {
@ -343,7 +187,7 @@ const goodsIds: any = computed({
},
set (value) {
emit('update:modelValue', value)
},
}
})
const showDialog = ref(false)
@ -373,11 +217,11 @@ const goodsTable = reactive({
verify_goods_ids: '',
verify_sku_ids: '',
goods_type: '',
is_gift: 0,
},
is_gift: 0
}
})
goodsTable.searchParam.is_gift = prop.isGift ? prop.isGift : 0
goodsTable.searchParam.is_gift = prop.isGift ? prop.isGift : 0;
const searchFormRef = ref()
@ -405,21 +249,17 @@ const initData = () => {
item.child_list.forEach((childItem: any) => {
children.push({
value: childItem.category_id,
label: childItem.category_name,
label: childItem.category_name
})
})
}
goodsCategoryTree.push({
value: item.category_id,
label: item.category_name,
children,
children
})
})
goodsCategoryOptions.splice(
0,
goodsCategoryOptions.length,
...goodsCategoryTree
)
goodsCategoryOptions.splice(0, goodsCategoryOptions.length, ...goodsCategoryTree)
}
})
@ -443,18 +283,18 @@ const multipleSelection: any = ref([])
//
const secondLevelArrowChange = (data)=>{
data.isShow = !data.isShow
data.isShow = !data.isShow;
}
//
const handleCheckAllChange = (isSelect) =>{
isStairIndeterminate.value = false
isStairIndeterminate.value = false;
goodsTable.data.forEach((item,index) => {
item.secondLevelCheckAll = isSelect
item.secondLevelCheckAll = isSelect;
item.skuList.forEach((subItem, subIndex) => {
subItem.threeLevelCheckAll = isSelect
})
})
subItem.threeLevelCheckAll = isSelect;
});
});
if (isSelect) {
goodsTable.data.forEach((item: any) => {
if(prop.mode == 'spu') {
@ -462,15 +302,13 @@ const handleCheckAllChange = (isSelect) => {
selectGoodsId.push(item.goods_id)
}else{
item.skuList.forEach((skuItem:any)=>{
selectGoodsId.push(skuItem.sku_id)
selectGoods[replacePrefix + skuItem.sku_id] = deepClone(skuItem)
selectGoods[replacePrefix + skuItem.sku_id].goods_name =
item.goods_name //
selectGoods[replacePrefix + skuItem.sku_id].goods_type_name =
item.goods_type_name
selectGoods[replacePrefix + skuItem.sku_id].goods_type =
item.goods_type
selectGoodsId.push(skuItem.sku_id);
selectGoods[replacePrefix + skuItem.sku_id] = deepClone(skuItem);
selectGoods[replacePrefix + skuItem.sku_id].goods_name = item.goods_name; //
selectGoods[replacePrefix + skuItem.sku_id].goods_type_name = item.goods_type_name;
selectGoods[replacePrefix + skuItem.sku_id].goods_type = item.goods_type;
})
}
})
} else {
@ -491,10 +329,11 @@ const handleCheckAllChange = (isSelect) => {
//
const secondLevelHandleCheckAllChange = (isSelect,row)=>{
row.skuList.forEach((item,index) => {
item.threeLevelCheckAll = isSelect
})
detectionAllSelect()
item.threeLevelCheckAll = isSelect;
});
detectionAllSelect();
if(prop.mode == 'spu'){
if (isSelect) {
selectGoodsId.push(row.goods_id)
@ -507,19 +346,18 @@ const secondLevelHandleCheckAllChange = (isSelect, row) => {
}else{
if (isSelect) {
row.skuList.forEach((item,index) => {
selectGoodsId.push(item.sku_id)
selectGoods[replacePrefix + item.sku_id] = deepClone(item)
selectGoods[replacePrefix + item.sku_id].goods_name = row.goods_name //
selectGoods[replacePrefix + item.sku_id].goods_type_name =
row.goods_type_name
selectGoods[replacePrefix + item.sku_id].goods_type = row.goods_type
})
selectGoodsId.push(item.sku_id);
selectGoods[replacePrefix + item.sku_id] = deepClone(item);
selectGoods[replacePrefix + item.sku_id].goods_name = row.goods_name; //
selectGoods[replacePrefix + item.sku_id].goods_type_name = row.goods_type_name;
selectGoods[replacePrefix + item.sku_id].goods_type = row.goods_type;
});
} else {
row.skuList.forEach((item,index) => {
selectGoodsId.splice(selectGoodsId.indexOf(item.sku_id),1)
//
delete selectGoods[replacePrefix + item.sku_id]
})
});
}
}
@ -528,61 +366,52 @@ const secondLevelHandleCheckAllChange = (isSelect, row) => {
// goodsListTableRef.value.toggleRowExpansion(...Object.values(spreadTableData),true)
// }, 0);
// prop.maxprop.max
if (
prop.max &&
prop.max > 0 &&
Object.keys(selectGoods).length > 0 &&
Object.keys(selectGoods).length > prop.max
) {
let len = Object.keys(selectGoods).length
len = len - prop.max
let goodsIdCopy = cloneDeep(selectGoodsId)
if(prop.max && prop.max > 0 && Object.keys(selectGoods).length > 0 && Object.keys(selectGoods).length > prop.max){
let len = Object.keys(selectGoods).length;
len = len - prop.max;
let goodsIdCopy = cloneDeep(selectGoodsId);
goodsIdCopy.forEach((item,index,arr) => {
if(index < len){
let indent = selectGoodsId.indexOf(item)
delete selectGoods[replacePrefix+selectGoodsId[indent]]
selectGoodsId.splice(indent,1)
}
})
setGoodsSelected()
});
setGoodsSelected();
}
}
//
const subChildHandleCheckAllChange = (
selected: any,
parentData: any,
data: any
) => {
let selectNum = 0
const subChildHandleCheckAllChange = (selected: any,parentData: any,data: any)=>{
let selectNum = 0;
parentData.skuList.forEach((item,index) => {
if(item.threeLevelCheckAll){
selectNum++
selectNum++;
}
})
});
if(selectNum > 0 && selectNum != parentData.skuList.length){
parentData.secondLevelCheckAll = false
parentData.isSecondLevelIndeterminate = true
parentData.secondLevelCheckAll = false;
parentData.isSecondLevelIndeterminate = true;
}else if(selectNum == parentData.skuList.length){
parentData.isSecondLevelIndeterminate = false
parentData.isSecondLevelIndeterminate = false;
parentData.secondLevelCheckAll = true
}else{
parentData.isSecondLevelIndeterminate = false
parentData.secondLevelCheckAll = false
parentData.isSecondLevelIndeterminate = false;
parentData.secondLevelCheckAll = false;
}
detectionAllSelect()
detectionAllSelect();
let currSku = deepClone(data)
if(selected){
selectGoodsId.push(currSku.sku_id)
selectGoodsId.push(currSku.sku_id);
currSku.goods_name = parentData.goods_name //
currSku.goods_type_name = parentData.goods_type_name
currSku.goods_type = parentData.goods_type
selectGoods[replacePrefix + currSku.sku_id] = currSku
currSku.goods_name = parentData.goods_name; //
currSku.goods_type_name = parentData.goods_type_name;
currSku.goods_type = parentData.goods_type;
selectGoods[replacePrefix + currSku.sku_id] = currSku;
}else{
selectGoodsId.splice(selectGoodsId.indexOf(currSku.sku_id),1)
//
@ -592,22 +421,22 @@ const subChildHandleCheckAllChange = (
//
const detectionAllSelect = ()=> {
let selectNum = 0
let selectNum = 0;
goodsTable.data.forEach((item,index) => {
if(item.secondLevelCheckAll){
selectNum++
selectNum++;
}
})
});
if(selectNum > 0 && selectNum != goodsTable.data.length){
staircheckAll.value = false
isStairIndeterminate.value = true
staircheckAll.value = false;
isStairIndeterminate.value = true;
}else if(selectNum > 0 && selectNum == goodsTable.data.length){
isStairIndeterminate.value = false
isStairIndeterminate.value = false;
staircheckAll.value = true
}else{
isStairIndeterminate.value = false
staircheckAll.value = false
isStairIndeterminate.value = false;
staircheckAll.value = false;
}
}
@ -615,13 +444,13 @@ const detectionAllSelect = () => {
* 获取商品列表
*/
const loadGoodsList = (page: number = 1, callback: any = null) => {
isStairIndeterminate.value = false
staircheckAll.value = false
goodsTable.loading = true
goodsTable.data = []
isStairIndeterminate.value = false;
staircheckAll.value = false;
goodsTable.loading = true;
goodsTable.data = [];
goodsTable.page = page
const searchData = cloneDeep(goodsTable.searchParam)
const searchData = cloneDeep(goodsTable.searchParam);
if (searchData.select_type == 'selected') {
const goods_ids = <any>[]
@ -630,85 +459,79 @@ const loadGoodsList = (page: number = 1, callback: any = null) => {
}
searchData[replacePrefix+'ids'] = goods_ids
} else {
searchData[replacePrefix + 'ids'] = ''
searchData[replacePrefix+'ids'] = '';
}
getGoodsSelectPageList({
page: goodsTable.page,
limit: goodsTable.limit,
...searchData,
})
.then((res) => {
let goodsTableData = cloneDeep(res.data.data)
...searchData
}).then(res => {
let goodsTableData = cloneDeep(res.data.data);
goodsTableData.forEach((item: any) => {
item.isShow = false
item.isSecondLevelIndeterminate = false
item.secondLevelCheckAll = false
item.isShow = false;
item.isSecondLevelIndeterminate = false;
item.secondLevelCheckAll = false;
})
if (prop.mode == 'sku') {
if(prop.mode == "sku") {
goodsTableData.forEach((item: any) => {
if (item.skuList.length) {
item.skuList.forEach((skuItem: any) => {
skuItem.threeLevelCheckAll = false
skuItem.goods_type = item.goods_type
skuItem.threeLevelCheckAll = false;
skuItem.goods_type = item.goods_type;
})
}
})
}
if (callback)
callback(
prop.mode == 'spu'
? res.data.verify_goods_ids
: res.data.verify_sku_ids,
res.data.select_goods_list
)
setGoodsSelected()
if (callback) callback(prop.mode == "spu" ? res.data.verify_goods_ids : res.data.verify_sku_ids, res.data.select_goods_list)
setGoodsSelected();
goodsTable.data = goodsTableData
goodsTable.total = res.data.total
goodsTable.loading = false
})
.catch(() => {
}).catch(() => {
goodsTable.loading = false
})
}
// spu
const setGoodsSelected = () => {
nextTick(() => {
if (prop.mode == 'spu') {
if(prop.mode == "spu"){
for (let i = 0; i < goodsTable.data.length; i++) {
goodsTable.data[i].secondLevelCheckAll = false
goodsTable.data[i].secondLevelCheckAll = false;
if (selectGoods[replacePrefix + goodsTable.data[i].goods_id]) {
goodsTable.data[i].secondLevelCheckAll = true
goodsTable.data[i].secondLevelCheckAll = true;
}
}
}else{
let isAllSelectSku = true
let isAllSelectSku = true;
for (let i = 0; i < goodsTable.data.length; i++) {
goodsTable.data[i].secondLevelCheckAll = false
goodsTable.data[i].secondLevelCheckAll = false;
isAllSelectSku = true
isAllSelectSku = true;
goodsTable.data[i].isSecondLevelIndeterminate = false
goodsTable.data[i].isSecondLevelIndeterminate = false;
goodsTable.data[i].skuList.forEach((item,index) => {
item.threeLevelCheckAll = false
item.threeLevelCheckAll = false;
if (selectGoods[replacePrefix + item.sku_id]) {
goodsTable.data[i].isSecondLevelIndeterminate = true
item.threeLevelCheckAll = true
goodsTable.data[i].isSecondLevelIndeterminate = true;
item.threeLevelCheckAll = true;
}else{
isAllSelectSku = false
isAllSelectSku = false;
}
})
});
if(isAllSelectSku){
goodsTable.data[i].isSecondLevelIndeterminate = false
goodsTable.data[i].secondLevelCheckAll = true
goodsTable.data[i].isSecondLevelIndeterminate = false;
goodsTable.data[i].secondLevelCheckAll = true;
}
}
}
detectionAllSelect()
})
detectionAllSelect();
});
}
const resetForm = (formEl: FormInstance | undefined) => {
@ -720,30 +543,31 @@ const resetForm = (formEl: FormInstance | undefined) => {
const show = () => {
for (let k in selectGoods) {
delete selectGoods[k]
delete selectGoods[k];
}
replacePrefix = prop.mode == 'sku' ? 'sku_' : 'goods_'
replacePrefix = prop.mode == "sku" ? 'sku_' : 'goods_';
// idid
if (prop.mode == 'sku') {
goodsTable.searchParam.verify_sku_ids = goodsIds.value
goodsTable.searchParam.verify_sku_ids = goodsIds.value;
} else {
goodsTable.searchParam.verify_goods_ids = goodsIds.value
goodsTable.searchParam.verify_goods_ids = goodsIds.value;
}
getGoodsSkuNoPageListFn()
getGoodsSkuNoPageListFn();
loadGoodsList(1, (verify_ids: any) => {
//
if (goodsIds.value && goodsIds.value.length) {
goodsIds.value.splice(0, goodsIds.value.length, ...verify_ids)
selectGoodsId.splice(0, selectGoodsId.length, ...verify_ids)
if(Object.keys(selectGoods).length){
for(let key in selectGoods){
let num = Number(key.split(replacePrefix)[1])
let num = Number(key.split(replacePrefix)[1]);
if(goodsIds.value.indexOf(num) == -1){
delete selectGoods[key]
delete selectGoods[key];
}
}
}
@ -754,49 +578,49 @@ const show = () => {
}
const getGoodsSkuNoPageListFn = () =>{
const searchData = cloneDeep(goodsTable.searchParam)
const searchData = cloneDeep(goodsTable.searchParam);
getGoodsSkuNoPageList({...searchData}).then((res:any)=>{
const selectGoodsData = res.data
const selectGoodsData = res.data;
//
if (prop.mode == 'sku') {
for (let i = 0; i < selectGoodsData.length; i++) {
selectGoodsData[i].skuList.forEach((item: any) => {
if (goodsIds.value.indexOf(item.sku_id) != -1) {
item.goods_name = selectGoodsData[i].goods_name //
item.goods_type_name = selectGoodsData[i].goods_type_name
item.goods_type = selectGoodsData[i].goods_type
selectGoods[replacePrefix + item.sku_id] = item
item.goods_name = selectGoodsData[i].goods_name; //
item.goods_type_name = selectGoodsData[i].goods_type_name;
item.goods_type = selectGoodsData[i].goods_type;
selectGoods[replacePrefix + item.sku_id] = item;
}
})
});
}
} else {
for (let i = 0; i < selectGoodsData.length; i++) {
if (goodsIds.value.indexOf(selectGoodsData[i].goods_id) != -1) {
selectGoods[replacePrefix + selectGoodsData[i].goods_id] =
selectGoodsData[i]
selectGoods[replacePrefix + selectGoodsData[i].goods_id] = selectGoodsData[i];
}
}
}
if(Object.keys(selectGoods).length && goodsIds.value.length){
for(let key in selectGoods){
let num = Number(key.split(replacePrefix)[1])
let num = Number(key.split(replacePrefix)[1]);
if(goodsIds.value.indexOf(num) == -1){
delete selectGoods[key]
delete selectGoods[key];
}
}
}
setGoodsSelected()
setGoodsSelected();
})
}
//
const clear = () => {
for (let k in selectGoods) {
delete selectGoods[k]
delete selectGoods[k];
}
setGoodsSelected()
setGoodsSelected();
}
const save = () => {
@ -804,72 +628,64 @@ const save = () => {
ElMessage({
type: 'warning',
message: `${t('goodsSelectPopupGoodsMinTip')}${prop.min}${t('goodsSelectPopupPiece')}`,
})
return
});
return;
}
if (
prop.max &&
prop.max > 0 &&
selectGoodsNum.value &&
selectGoodsNum.value > prop.max
) {
if (prop.max && prop.max > 0 && selectGoodsNum.value && selectGoodsNum.value > prop.max) {
ElMessage({
type: 'warning',
message: `${t('goodsSelectPopupGoodsMaxTip')}${prop.max}${t('goodsSelectPopupPiece')}`,
})
return
});
return;
}
if(prop.way == 'single'){
let realTypeNum = 0
let virtualTypeNum = 0
let realTypeNum = 0;
let virtualTypeNum = 0;
for (let k in selectGoods) {
if (selectGoods[k].goods_type == 'virtual') {
virtualTypeNum++
} else if (selectGoods[k].goods_type == 'real') {
realTypeNum++
if(selectGoods[k].goods_type == "virtual"){
virtualTypeNum++;
}else if(selectGoods[k].goods_type == "real"){
realTypeNum++;
}
}
if (
realTypeNum != Object.keys(selectGoods).length &&
virtualTypeNum != Object.keys(selectGoods).length
) {
if (realTypeNum != Object.keys(selectGoods).length && virtualTypeNum != Object.keys(selectGoods).length) {
ElMessage({
type: 'warning',
message: `${t('wayPlaceholder')}`,
})
return
});
return;
}
}
let ids: any = []
let ids: any = [];
for (let k in selectGoods) {
ids.push(parseInt(k.replace(replacePrefix, '')))
ids.push(parseInt(k.replace(replacePrefix, '')));
}
goodsIds.value.splice(0, goodsIds.value.length, ...ids)
emit('goodsSelect',selectGoods)
initSearchParam()
initSearchParam();
showDialog.value = false
}
//
const initSearchParam = ()=>{
goodsTable.searchParam.keyword = ''
goodsTable.searchParam.goods_category = []
goodsTable.searchParam.select_type = 'all'
goodsTable.searchParam.goods_ids = ''
goodsTable.searchParam.verify_goods_ids = ''
goodsTable.searchParam.verify_sku_ids = ''
goodsTable.searchParam.goods_type = ''
goodsTable.searchParam.keyword = '';
goodsTable.searchParam.goods_category = [];
goodsTable.searchParam.select_type = 'all';
goodsTable.searchParam.goods_ids = '';
goodsTable.searchParam.verify_goods_ids = '';
goodsTable.searchParam.verify_sku_ids = '';
goodsTable.searchParam.goods_type = '';
}
defineExpose({
showDialog,
selectGoods,
selectGoodsNum,
selectGoodsNum
})
</script>

34
admin/src/addon/shop/views/goods/components/goods-spread-popup.vue

@ -1,15 +1,9 @@
<template>
<div>
<el-dialog
v-model="showDialog"
:title="t('goodsSpreadTitle')"
width="500px"
:destroy-on-close="true"
>
<el-dialog v-model="showDialog" :title="t('goodsSpreadTitle')" width="500px" :destroy-on-close="true">
<div class="promote-flex flex">
<div
class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]"
>
<div class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]">
<el-image :src="wapImage" />
</div>
@ -17,16 +11,14 @@
<div class="mb-[10px]">{{ t('spreadLink') }}</div>
<el-input class="mb-[10px]" readonly :value="wapPreview">
<template #append>
<el-button @click="copyEvent(wapPreview)">{{
t('copy')
}}</el-button>
<el-button @click="copyEvent(wapPreview)">{{ t('copy') }}</el-button>
</template>
</el-input>
<a class="text-primary" :href="wapImage" download>{{
t('downloadQrcode')
}}</a>
<a class="text-primary" :href="wapImage" download>{{ t('downloadQrcode') }}</a>
</div>
</div>
</el-dialog>
</div>
</template>
@ -73,11 +65,7 @@ getUrl().then((res: any) => {
const loadQrcode = () => {
wapPreview.value = `${wapUrl.value}${page.value}`
// errorCorrectionLevelLH()
QRCode.toDataURL(wapPreview.value, {
errorCorrectionLevel: 'L',
margin: 0,
width: 120,
}).then((url) => {
QRCode.toDataURL(wapPreview.value, { errorCorrectionLevel: 'L', margin: 0, width: 120 }).then(url => {
wapImage.value = url
})
}
@ -100,7 +88,7 @@ const copyEvent = (text: string) => {
if (!isSupported.value) {
ElMessage({
message: t('notSupportCopy'),
type: 'warning',
type: 'warning'
})
}
copy(text)
@ -110,14 +98,14 @@ watch(copied, () => {
if (copied.value) {
ElMessage({
message: t('copySuccess'),
type: 'success',
type: 'success'
})
}
})
defineExpose({
showDialog,
show,
show
})
</script>

116
admin/src/addon/shop/views/goods/components/goods-stock-edit-popup.vue

@ -1,94 +1,44 @@
<template>
<div>
<el-dialog
v-model="showDialog"
:title="t('editStockPopupTitle')"
width="600px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-dialog v-model="showDialog" :title="t('editStockPopupTitle')" width="600px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<div class="flex items-center mb-[10px]">
<div class="min-w-[70px] h-[70px] flex items-center justify-center">
<el-image
v-if="goods.goods_cover_thumb_small"
class="w-[70px] h-[70px]"
:src="img(goods.goods_cover_thumb_small)"
fit="contain"
>
<el-image v-if="goods.goods_cover_thumb_small" class="w-[70px] h-[70px]" :src="img(goods.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="goods.goods_name" class="multi-hidden">{{
goods.goods_name
}}</span>
<span class="text-primary text-[12px]">{{
goods.goods_type_name
}}</span>
<span :title="goods.goods_name" class="multi-hidden">{{ goods.goods_name }}</span>
<span class="text-primary text-[12px]">{{ goods.goods_type_name }}</span>
</div>
</div>
<!-- 批量设置 -->
<div
class="batch-operation-sku"
v-if="activeGoodsCount == 0 && goodsTable.data.length > 1"
>
<div class="batch-operation-sku" v-if="activeGoodsCount == 0 && goodsTable.data.length > 1">
<label>{{ t('batchOperationSku') }}</label>
<el-input
v-model.trim="batchOperation.value"
clearable
:placeholder="t('stock')"
class="set-input"
maxlength="8"
:autofocus="true"
/>
<el-button type="primary" @click="saveBatch">{{
t('confirm')
}}</el-button>
<el-input v-model.trim="batchOperation.value" clearable :placeholder="t('stock')" class="set-input" maxlength="8" :autofocus="true" />
<el-button type="primary" @click="saveBatch">{{ t('confirm') }}</el-button>
</div>
<el-table
:data="goodsTable.data"
size="large"
v-loading="goodsTable.loading"
max-height="400"
>
<el-table :data="goodsTable.data" size="large" v-loading="goodsTable.loading" max-height="400">
<template #empty>
<span>{{ !goodsTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="sku_name"
:label="t('skuName')"
min-width="120"
v-if="goodsTable.data.length > 1"
/>
<el-table-column prop="sku_name" :label="t('skuName')" min-width="120" v-if="goodsTable.data.length > 1" />
<el-table-column prop="price" :label="t('price')" min-width="120" />
<el-table-column prop="stock" :label="t('stock')" min-width="120">
<template #default="{ row }">
<el-input
v-model.trim="row.stock"
clearable
placeholder="0"
maxlength="8"
:disabled="activeGoodsCount > 0"
/>
<el-input v-model.trim="row.stock" clearable placeholder="0" maxlength="8" :disabled="activeGoodsCount > 0" />
</template>
</el-table-column>
</el-table>
<template #footer>
@ -109,7 +59,7 @@ import { ElMessage } from 'element-plus'
import {
getActiveGoodsCount,
getGoodsSkuList,
editGoodsListStock,
editGoodsListStock
} from '@/addon/shop/api/goods'
const goods: any = reactive({})
@ -125,18 +75,18 @@ interface goodsTableType {
}
const goodsTable = reactive<goodsTableType>({
loading: true,
data: [],
data: []
})
const batchOperation: any = reactive({
value: '', //
value: '' //
})
const saveBatch = () => {
if (batchOperation.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('stockPlaceholder')}`,
message: `${t('stockPlaceholder')}`
})
return
}
@ -144,7 +94,7 @@ const saveBatch = () => {
if (!regExp.number.test(batchOperation.value)) {
ElMessage({
type: 'warning',
message: `${t('stockTips')}`,
message: `${t('stockTips')}`
})
return
}
@ -152,7 +102,7 @@ const saveBatch = () => {
if (batchOperation.value < 0) {
ElMessage({
type: 'warning',
message: `${t('stockNotZeroTips')}`,
message: `${t('stockNotZeroTips')}`
})
return
}
@ -169,13 +119,11 @@ const loadGoodsList = () => {
goodsTable.loading = true
getGoodsSkuList({
goods_id: goods.goods_id,
})
.then((res) => {
goods_id: goods.goods_id
}).then(res => {
goodsTable.loading = false
goodsTable.data = res.data
})
.catch(() => {
}).catch(() => {
goodsTable.loading = false
})
}
@ -189,15 +137,15 @@ const show = (data: any) => {
const getActiveGoodsCountFn = ()=>{
getActiveGoodsCount({
goods_id: goods.goods_id,
goods_id: goods.goods_id
}).then((res)=>{
activeGoodsCount.value = res.data
activeGoodsCount.value = res.data;
})
}
//
const regExp: any = {
number: /^\d{0,10}$/,
number: /^\d{0,10}$/
}
const verify = () => {
@ -209,14 +157,14 @@ const verify = () => {
result = false
ElMessage({
type: 'warning',
message: `${t('stockPlaceholder')}`,
message: `${t('stockPlaceholder')}`
})
break
} else if (isNaN(item.stock) || !regExp.number.test(item.stock)) {
result = false
ElMessage({
type: 'warning',
message: `${t('stockTips')}`,
message: `${t('stockTips')}`
})
break
} else if (item.stock < 0) {
@ -238,13 +186,13 @@ const save = () => {
goodsTable.data.forEach((item: any) => {
sku_list.push({
sku_id: item.sku_id,
stock: item.stock,
stock: item.stock
})
})
editGoodsListStock({
goods_id: goods.goods_id,
sku_list,
}).then((res) => {
sku_list
}).then(res => {
emit('load')
showDialog.value = false
})
@ -253,7 +201,7 @@ const save = () => {
defineExpose({
showDialog,
show,
show
})
</script>

120
admin/src/addon/shop/views/goods/components/label-edit.vue

@ -1,41 +1,12 @@
<template>
<el-dialog
v-model="showDialog"
:title="title"
width="500px"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-dialog v-model="showDialog" :title="title" width="500px" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('labelName')" prop="label_name">
<el-input
v-model.trim="formData.label_name"
clearable
:placeholder="t('labelNamePlaceholder')"
class="input-width"
maxlength="10"
/>
<el-input v-model.trim="formData.label_name" clearable :placeholder="t('labelNamePlaceholder')" class="input-width" maxlength="10" />
</el-form-item>
<el-form-item :label="t('groupName')" prop="group_id">
<el-select
v-model="formData.group_id"
:placeholder="t('groupNamePlaceholder')"
clearable
class="input-width"
>
<el-option
v-for="item in prop.groupList"
:key="item.group_id"
:label="item.group_name"
:value="item.group_id"
/>
<el-select v-model="formData.group_id" :placeholder="t('groupNamePlaceholder')" clearable class="input-width">
<el-option v-for="item in prop.groupList" :key="item.group_id" :label="item.group_name" :value="item.group_id" />
</el-select>
</el-form-item>
<el-form-item :label="t('styleType')">
@ -44,34 +15,16 @@
<el-radio label="icon">{{ t('styleByIcon') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('textColor')"
v-show="formData.style_type == 'diy'"
>
<el-color-picker
v-model="formData.color_json.text_color"
show-alpha
:predefine="predefineColors"
/>
<el-form-item :label="t('textColor')" v-show="formData.style_type == 'diy'">
<el-color-picker v-model="formData.color_json.text_color" show-alpha :predefine="predefineColors" />
<div class="form-tip">{{ t('colorTips') }}</div>
</el-form-item>
<el-form-item :label="t('bgColor')" v-show="formData.style_type == 'diy'">
<el-color-picker
v-model="formData.color_json.bg_color"
show-alpha
:predefine="predefineColors"
/>
<el-color-picker v-model="formData.color_json.bg_color" show-alpha :predefine="predefineColors" />
<div class="form-tip">{{ t('colorTips') }}</div>
</el-form-item>
<el-form-item
:label="t('borderColor')"
v-show="formData.style_type == 'diy'"
>
<el-color-picker
v-model="formData.color_json.border_color"
show-alpha
:predefine="predefineColors"
/>
<el-form-item :label="t('borderColor')" v-show="formData.style_type == 'diy'">
<el-color-picker v-model="formData.color_json.border_color" show-alpha :predefine="predefineColors" />
<div class="form-tip">{{ t('colorTips') }}</div>
</el-form-item>
@ -81,46 +34,21 @@
</el-form-item>
<el-form-item :label="t('status')">
<el-switch
v-model="formData.status"
:active-value="1"
:inactive-value="0"
/>
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item :label="t('memo')" >
<el-input
v-model.trim="formData.memo"
type="textarea"
clearable
:placeholder="t('memoPlaceholder')"
class="input-width"
maxlength="50"
/>
<el-input v-model.trim="formData.memo" type="textarea" clearable :placeholder="t('memoPlaceholder')" class="input-width" maxlength="50"/>
</el-form-item>
<el-form-item :label="t('sort')" >
<el-input
v-model.trim="formData.sort"
clearable
:placeholder="t('sortPlaceholder')"
class="input-width"
@keyup="filterNumber($event)"
maxlength="8"
show-word-limit
@blur="formData.sort = $event.target.value"
/>
<el-input v-model.trim="formData.sort" clearable :placeholder="t('sortPlaceholder')" class="input-width" @keyup="filterNumber($event)" maxlength="8" show-word-limit @blur="formData.sort = $event.target.value"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -136,7 +64,7 @@ import { addLabel, editLabel, getLabelInfo } from '@/addon/shop/api/goods'
const prop = defineProps({
groupList: {
type: Object,
default: [],
default: []
},
})
@ -160,7 +88,7 @@ const initialFormData = {
icon: '',
memo: '',
sort: '',
status: 1,
status: 1
}
const predefineColors = ref([
@ -181,7 +109,7 @@ const predefineColors = ref([
'rgb(255, 120, 0)',
'hsl(181, 100%, 37%)',
'hsla(209, 100%, 56%, 0.73)',
'#c7158577',
'#c7158577'
])
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -192,11 +120,11 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
label_name: [
{ required: true, message: t('labelNamePlaceholder'), trigger: 'blur' },
{ required: true, message: t('labelNamePlaceholder'), trigger: 'blur' }
],
group_id: [
{ required: true, message: t('groupNamePlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('groupNamePlaceholder'), trigger: 'blur' }
]
}
})
@ -216,13 +144,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
const data = formData
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
}).catch(err => {
loading.value = false
})
}
@ -249,7 +175,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>

63
admin/src/addon/shop/views/goods/components/label-group-edit.vue

@ -1,51 +1,18 @@
<template>
<el-dialog
v-model="showDialog"
:title="title"
width="500px"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-dialog v-model="showDialog" :title="title" width="500px" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('groupName')" prop="group_name">
<el-input
v-model.trim="formData.group_name"
clearable
:placeholder="t('groupNamePlaceholder')"
class="input-width"
maxlength="10"
/>
<el-input v-model.trim="formData.group_name" clearable :placeholder="t('groupNamePlaceholder')" class="input-width" maxlength="10" />
</el-form-item>
<el-form-item :label="t('sort')" >
<el-input
v-model.trim="formData.sort"
clearable
:placeholder="t('sortPlaceholder')"
class="input-width"
@keyup="filterNumber($event)"
maxlength="8"
show-word-limit
@blur="formData.sort = $event.target.value"
/>
<el-input v-model.trim="formData.sort" clearable :placeholder="t('sortPlaceholder')" class="input-width" @keyup="filterNumber($event)" maxlength="8" show-word-limit @blur="formData.sort = $event.target.value"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -56,11 +23,7 @@ import { ref, reactive, computed } from 'vue'
import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { filterNumber } from '@/utils/common'
import {
addLabelGroup,
editLabelGroup,
getLabelGroupInfo,
} from '@/addon/shop/api/goods'
import { addLabelGroup, editLabelGroup, getLabelGroupInfo } from '@/addon/shop/api/goods'
const showDialog = ref(false)
const loading = ref(false)
@ -72,7 +35,7 @@ const title = ref('')
const initialFormData = {
group_id: '',
group_name: '',
sort: '',
sort: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -82,8 +45,8 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
group_name: [
{ required: true, message: t('groupNamePlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('groupNamePlaceholder'), trigger: 'blur' }
]
}
})
@ -103,13 +66,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
const data = formData
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch((err) => {
}).catch(err => {
loading.value = false
})
}
@ -136,7 +97,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>

578
admin/src/addon/shop/views/goods/components/newcomer-goods-select-popup.vue

@ -3,90 +3,38 @@
<div @click="show">
<slot>
<el-button>{{ t('goodsSelectPopupSelectGoodsButton') }}</el-button>
<div
class="inline-block ml-[10px] text-[14px]"
v-show="goodsIds.length"
>
<div class="inline-block ml-[10px] text-[14px]" v-show="goodsIds.length">
<span>{{ t('goodsSelectPopupSelect') }}</span>
<span class="text-primary mx-[2px]">{{ goodsIds.length }}</span>
<span>{{ t('goodsSelectPopupPiece') }}</span>
</div>
</slot>
</div>
<el-dialog
v-model="showDialog"
:title="t('goodsSelectPopupSelectGoodsDialog')"
width="1000px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-form
:inline="true"
:model="goodsTable.searchParam"
ref="searchFormRef"
>
<el-dialog v-model="showDialog" :title="t('goodsSelectPopupSelectGoodsDialog')" width="1000px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-form :inline="true" :model="goodsTable.searchParam" ref="searchFormRef">
<el-form-item prop="select_type" class="form-item-wrap">
<el-select
v-model="goodsTable.searchParam.select_type"
@change="handleSelectTypeChange"
>
<el-select v-model="goodsTable.searchParam.select_type" @change="handleSelectTypeChange">
<el-option :label="t('goodsSelectPopupAllGoods')" value="all" />
<el-option
:label="t('goodsSelectPopupSelectedGoods')"
value="selected"
/>
<el-option :label="t('goodsSelectPopupSelectedGoods')" value="selected" />
</el-select>
</el-form-item>
<el-form-item
:label="t('goodsSelectPopupGoodsName')"
prop="keyword"
class="form-item-wrap"
>
<el-input
v-model.trim="goodsTable.searchParam.keyword"
:placeholder="t('goodsSelectPopupGoodsNamePlaceholder')"
maxlength="60"
/>
<el-form-item :label="t('goodsSelectPopupGoodsName')" prop="keyword" class="form-item-wrap">
<el-input v-model.trim="goodsTable.searchParam.keyword" :placeholder="t('goodsSelectPopupGoodsNamePlaceholder')" maxlength="60" />
</el-form-item>
<el-form-item
:label="t('goodsSelectPopupGoodsCategory')"
prop="goods_category"
class="form-item-wrap"
>
<el-cascader
v-model="goodsTable.searchParam.goods_category"
:options="goodsCategoryOptions"
:placeholder="t('goodsSelectPopupGoodsCategoryPlaceholder')"
clearable
:props="{ value: 'value', label: 'label', emitPath: false }"
/>
<el-form-item :label="t('goodsSelectPopupGoodsCategory')" prop="goods_category" class="form-item-wrap">
<el-cascader v-model="goodsTable.searchParam.goods_category"
:options="goodsCategoryOptions" :placeholder="t('goodsSelectPopupGoodsCategoryPlaceholder')"
clearable :props="{ value: 'value', label: 'label', emitPath:false }" />
</el-form-item>
<el-form-item
:label="t('goodsSelectPopupGoodsType')"
prop="goods_type"
class="form-item-wrap"
>
<el-select
v-model="goodsTable.searchParam.goods_type"
:placeholder="t('goodsSelectPopupGoodsTypePlaceholder')"
clearable
>
<el-option
v-for="item in goodsType"
:key="item.type"
:label="item.name"
:value="item.type"
/>
<el-form-item :label="t('goodsSelectPopupGoodsType')" prop="goods_type" class="form-item-wrap">
<el-select v-model="goodsTable.searchParam.goods_type" :placeholder="t('goodsSelectPopupGoodsTypePlaceholder')" clearable>
<el-option v-for="item in goodsType" :key="item.type" :label="item.name" :value="item.type" />
</el-select>
</el-form-item>
<el-form-item class="form-item-wrap">
<el-button type="primary" @click="loadGoodsList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadGoodsList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
@ -94,147 +42,63 @@
<div class="table-head flex items-center bg-[#f5f7f9] py-[8px]">
<div class="w-[3%]"></div>
<div class="w-[7%]">
<el-checkbox
v-model="staircheckAll"
:indeterminate="isStairIndeterminate"
@change="handleCheckAllChange"
/>
<el-checkbox v-model="staircheckAll" :indeterminate="isStairIndeterminate" @change="handleCheckAllChange" />
</div>
<div class="w-[50%]">商品信息</div>
<div class="w-[20%]">商品价格</div>
<div class="w-[20%]">库存</div>
</div>
<div class="table-body h-[350px] overflow-y-auto">
<div
v-for="(row, rowIndex) in goodsTable.data"
:key="rowIndex"
class="flex flex-col"
>
<div v-for="(row,rowIndex) in goodsTable.data" :key="rowIndex" class="flex flex-col">
<!-- 内容 -->
<div
class="flex items-center border-solid border-[#e5e7eb] py-[8px] border-b-[1px]"
>
<div class="flex items-center border-solid border-[#e5e7eb] py-[8px] border-b-[1px]">
<div v-if="prop.mode == 'spu'" class="w-[3%]"></div>
<div
v-if="prop.mode == 'sku' && row.skuList.length > 1"
class="w-[3%] cursor-pointer text-center !text-[10px]"
@click="secondLevelArrowChange(row)"
:class="{
'iconfont iconxiangyoujiantou': row.skuList.length,
'arrow-show': row.isShow,
}"
></div>
<div
v-if="prop.mode == 'sku' && row.skuList.length <= 1"
class="w-[3%]"
></div>
<div v-if="prop.mode == 'sku' && row.skuList.length > 1" class="w-[3%] cursor-pointer text-center !text-[10px]" @click="secondLevelArrowChange(row)" :class="{'iconfont iconxiangyoujiantou': row.skuList.length, 'arrow-show': row.isShow}"></div>
<div v-if="prop.mode == 'sku' && row.skuList.length <= 1" class="w-[3%]"></div>
<div class="w-[7%]">
<el-checkbox
v-model="row.secondLevelCheckAll"
:indeterminate="row.isSecondLevelIndeterminate"
@change="secondLevelHandleCheckAllChange($event, row)"
/>
<el-checkbox v-model="row.secondLevelCheckAll" :indeterminate="row.isSecondLevelIndeterminate" @change="secondLevelHandleCheckAllChange($event,row)" />
</div>
<div class="flex items-center cursor-pointer w-[50%]">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.goods_cover_thumb_small"
class="w-[60px] h-[60px]"
:src="img(row.goods_cover_thumb_small)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.goods_cover_thumb_small" class="w-[60px] h-[60px]" :src="img(row.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span
:title="row.goods_name"
class="multi-hidden leading-[1.4]"
>{{ row.goods_name }}</span
>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
</div>
<span :title="row.goods_name" class="multi-hidden leading-[1.4]">{{ row.goods_name }}</span>
<span class="text-primary text-[12px]">{{ row.goods_type_name }}</span>
</div>
<div class="w-[20%]">
{{
row.skuList.length > 1
? row.goodsSku.price
: row.goodsSku.newcomer_price
}}
</div>
<div class="w-[20%]">{{ row.skuList.length > 1 ? row.goodsSku.price : row.goodsSku.newcomer_price }}</div>
<div class="w-[20%]">{{row.stock}}</div>
</div>
<div v-show="prop.mode == 'sku' && row.skuList.length > 1">
<!-- 子级 -->
<div
v-for="(item, index) in row.skuList"
:key="index"
class="flex items-center py-[8px] border-solid border-transparent border-b-[1px]"
:class="{
hidden: !row.isShow,
'border-[#e5e7eb]': index == row.skuList.length - 1,
}"
>
<div v-for="(item,index) in row.skuList" :key="index" class="flex items-center py-[8px] border-solid border-transparent border-b-[1px]" :class="{'hidden': !row.isShow, 'border-[#e5e7eb]': index == (row.skuList.length-1)}">
<div class="w-[6%]"></div>
<div class="w-[4%]">
<el-checkbox
v-model="item.threeLevelCheckAll"
@change="subChildHandleCheckAllChange($event, row, item)"
/>
<el-checkbox v-model="item.threeLevelCheckAll" @change="subChildHandleCheckAllChange($event,row,item)" />
</div>
<div class="flex items-center cursor-pointer w-[50%]">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="item.sku_image"
class="w-[60px] h-[60px]"
:src="img(item.sku_image)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="item.sku_image" class="w-[60px] h-[60px]" :src="img(item.sku_image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span
:title="item.sku_name || row.goods_name"
class="multi-hidden leading-[1.4]"
>{{ item.sku_name || row.goods_name }}</span
>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
<span :title="item.sku_name || row.goods_name" class="multi-hidden leading-[1.4]">{{ item.sku_name || row.goods_name }}</span>
<span class="text-primary text-[12px]">{{ row.goods_type_name }}</span>
</div>
</div>
<div class="w-[20%] flex">{{ item.newcomer_price }}</div>
@ -243,41 +107,25 @@
</div>
</div>
<div
v-if="!goodsTable.data.length && !goodsTable.loading"
class="h-[60px] flex items-center justify-center border-solid border-[#e5e7eb] py-[12px] border-b-[1px]"
>
<div v-if="!goodsTable.data.length && !goodsTable.loading" class="h-[60px] flex items-center justify-center border-solid border-[#e5e7eb] py-[12px] border-b-[1px]">
暂无数据
</div>
</div>
</div>
<div class="mt-[16px] flex">
<div class="flex items-center flex-1">
<div
class="layui-table-bottom-left-container mr-[10px]"
v-show="selectGoodsNum"
>
<div class="layui-table-bottom-left-container mr-[10px]" v-show="selectGoodsNum">
<span>{{ t('goodsSelectPopupBeforeTip') }}</span>
<span class="text-primary mx-[2px]">{{ selectGoodsNum }}</span>
<span>{{ t('goodsSelectPopupAfterTip') }}</span>
</div>
<el-button
type="primary"
link
@click="clear"
v-show="selectGoodsNum"
>{{ t('goodsSelectPopupClearGoods') }}</el-button
>
<el-button type="primary" link @click="clear" v-show="selectGoodsNum">{{ t('goodsSelectPopupClearGoods') }}</el-button>
</div>
<el-pagination
v-model:current-page="goodsTable.page"
v-model:page-size="goodsTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="goodsTable.total"
@size-change="loadGoodsList()"
@current-change="loadGoodsList"
/>
<el-pagination v-model:current-page="goodsTable.page" v-model:page-size="goodsTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="goodsTable.total"
@size-change="loadGoodsList()" @current-change="loadGoodsList" />
</div>
<template #footer>
@ -297,41 +145,39 @@ import { cloneDeep } from 'lodash-es'
import { img,deepClone } from '@/utils/common'
import { ElMessage } from 'element-plus'
import { getCategoryTree, getGoodsType } from '@/addon/shop/api/goods'
import {
getNewcomerSelectGoodsList,
getNewcomerGoodsList,
} from '@/addon/shop/api/marketing'
import { getNewcomerSelectGoodsList, getNewcomerGoodsList } from '@/addon/shop/api/marketing'
const prop = defineProps({
modelValue: {
type: String,
default: '',
default: ''
},
max: {
type: Number,
default: 0,
default: 0
},
min: {
type: Number,
default: 0,
default: 0
},
mode: {
type: String,
default: 'spu', // spusku
default: 'spu' // spusku
},
way: {
type: String,
default: '', // single
default: '' // single
},
})
const emit = defineEmits(['update:modelValue','goodsSelect'])
// prop.mode sku_goods_
let replacePrefix = prop.mode == 'sku' ? 'sku_' : 'goods_'
let replacePrefix = prop.mode == "sku" ? 'sku_' : 'goods_';
const isStairIndeterminate = ref(false)
const staircheckAll = ref(false)
const isStairIndeterminate = ref(false);
const staircheckAll = ref(false);
const goodsIds: any = computed({
get () {
@ -339,7 +185,7 @@ const goodsIds: any = computed({
},
set (value) {
emit('update:modelValue', value)
},
}
})
const showDialog = ref(false)
@ -368,8 +214,8 @@ const goodsTable = reactive({
goods_ids: '',
verify_goods_ids: '',
verify_sku_ids: '',
goods_type: '',
},
goods_type: ''
}
})
const searchFormRef = ref()
@ -398,21 +244,17 @@ const initData = () => {
item.child_list.forEach((childItem: any) => {
children.push({
value: childItem.category_id,
label: childItem.category_name,
label: childItem.category_name
})
})
}
goodsCategoryTree.push({
value: item.category_id,
label: item.category_name,
children,
children
})
})
goodsCategoryOptions.splice(
0,
goodsCategoryOptions.length,
...goodsCategoryTree
)
goodsCategoryOptions.splice(0, goodsCategoryOptions.length, ...goodsCategoryTree)
}
})
@ -434,20 +276,21 @@ const goodsListTableRef = ref()
//
const multipleSelection: any = ref([])
//
const secondLevelArrowChange = (data)=>{
data.isShow = !data.isShow
data.isShow = !data.isShow;
}
//
const handleCheckAllChange = (isSelect) =>{
isStairIndeterminate.value = false
isStairIndeterminate.value = false;
goodsTable.data.forEach((item,index) => {
item.secondLevelCheckAll = isSelect
item.secondLevelCheckAll = isSelect;
item.skuList.forEach((subItem, subIndex) => {
subItem.threeLevelCheckAll = isSelect
})
})
subItem.threeLevelCheckAll = isSelect;
});
});
if (isSelect) {
goodsTable.data.forEach((item: any) => {
if(prop.mode == 'spu') {
@ -455,15 +298,13 @@ const handleCheckAllChange = (isSelect) => {
selectGoodsId.push(item.goods_id)
}else{
item.skuList.forEach((skuItem:any)=>{
selectGoodsId.push(skuItem.sku_id)
selectGoods[replacePrefix + skuItem.sku_id] = deepClone(skuItem)
selectGoods[replacePrefix + skuItem.sku_id].goods_name =
item.goods_name //
selectGoods[replacePrefix + skuItem.sku_id].goods_type_name =
item.goods_type_name
selectGoods[replacePrefix + skuItem.sku_id].goods_type =
item.goods_type
selectGoodsId.push(skuItem.sku_id);
selectGoods[replacePrefix + skuItem.sku_id] = deepClone(skuItem);
selectGoods[replacePrefix + skuItem.sku_id].goods_name = item.goods_name; //
selectGoods[replacePrefix + skuItem.sku_id].goods_type_name = item.goods_type_name;
selectGoods[replacePrefix + skuItem.sku_id].goods_type = item.goods_type;
})
}
})
} else {
@ -484,10 +325,11 @@ const handleCheckAllChange = (isSelect) => {
//
const secondLevelHandleCheckAllChange = (isSelect,row)=>{
row.skuList.forEach((item,index) => {
item.threeLevelCheckAll = isSelect
})
detectionAllSelect()
item.threeLevelCheckAll = isSelect;
});
detectionAllSelect();
if(prop.mode == 'spu'){
if (isSelect) {
@ -501,19 +343,18 @@ const secondLevelHandleCheckAllChange = (isSelect, row) => {
}else{
if (isSelect) {
row.skuList.forEach((item,index) => {
selectGoodsId.push(item.sku_id)
selectGoods[replacePrefix + item.sku_id] = deepClone(item)
selectGoods[replacePrefix + item.sku_id].goods_name = row.goods_name //
selectGoods[replacePrefix + item.sku_id].goods_type_name =
row.goods_type_name
selectGoods[replacePrefix + item.sku_id].goods_type = row.goods_type
})
selectGoodsId.push(item.sku_id);
selectGoods[replacePrefix + item.sku_id] = deepClone(item);
selectGoods[replacePrefix + item.sku_id].goods_name = row.goods_name; //
selectGoods[replacePrefix + item.sku_id].goods_type_name = row.goods_type_name;
selectGoods[replacePrefix + item.sku_id].goods_type = row.goods_type;
});
} else {
row.skuList.forEach((item,index) => {
selectGoodsId.splice(selectGoodsId.indexOf(item.sku_id),1)
//
delete selectGoods[replacePrefix + item.sku_id]
})
});
}
}
@ -523,61 +364,53 @@ const secondLevelHandleCheckAllChange = (isSelect, row) => {
// }, 0);
// prop.maxprop.max
if (
prop.max &&
prop.max > 0 &&
Object.keys(selectGoods).length > 0 &&
Object.keys(selectGoods).length > prop.max
) {
let len = Object.keys(selectGoods).length
len = len - prop.max
let goodsIdCopy = cloneDeep(selectGoodsId)
if(prop.max && prop.max > 0 && Object.keys(selectGoods).length > 0 && Object.keys(selectGoods).length > prop.max){
let len = Object.keys(selectGoods).length;
len = len - prop.max;
let goodsIdCopy = cloneDeep(selectGoodsId);
goodsIdCopy.forEach((item,index,arr) => {
if(index < len){
let indent = selectGoodsId.indexOf(item)
delete selectGoods[replacePrefix+selectGoodsId[indent]]
selectGoodsId.splice(indent,1)
}
})
setGoodsSelected()
});
setGoodsSelected();
}
}
//
const subChildHandleCheckAllChange = (
selected: any,
parentData: any,
data: any
) => {
let selectNum = 0
const subChildHandleCheckAllChange = (selected: any,parentData: any,data: any)=>{
let selectNum = 0;
parentData.skuList.forEach((item,index) => {
if(item.threeLevelCheckAll){
selectNum++
selectNum++;
}
})
});
if(selectNum > 0 && selectNum != parentData.skuList.length){
parentData.secondLevelCheckAll = false
parentData.isSecondLevelIndeterminate = true
parentData.secondLevelCheckAll = false;
parentData.isSecondLevelIndeterminate = true;
}else if(selectNum == parentData.skuList.length){
parentData.isSecondLevelIndeterminate = false
parentData.isSecondLevelIndeterminate = false;
parentData.secondLevelCheckAll = true
}else{
parentData.isSecondLevelIndeterminate = false
parentData.secondLevelCheckAll = false
parentData.isSecondLevelIndeterminate = false;
parentData.secondLevelCheckAll = false;
}
detectionAllSelect()
detectionAllSelect();
let currSku = deepClone(data)
if(selected){
selectGoodsId.push(currSku.sku_id)
selectGoodsId.push(currSku.sku_id);
currSku.goods_name = parentData.goods_name //
currSku.goods_type_name = parentData.goods_type_name
currSku.goods_type = parentData.goods_type
selectGoods[replacePrefix + currSku.sku_id] = currSku
currSku.goods_name = parentData.goods_name; //
currSku.goods_type_name = parentData.goods_type_name;
currSku.goods_type = parentData.goods_type;
selectGoods[replacePrefix + currSku.sku_id] = currSku;
}else{
selectGoodsId.splice(selectGoodsId.indexOf(currSku.sku_id),1)
//
@ -585,24 +418,25 @@ const subChildHandleCheckAllChange = (
}
}
//
const detectionAllSelect = ()=> {
let selectNum = 0
let selectNum = 0;
goodsTable.data.forEach((item,index) => {
if(item.secondLevelCheckAll){
selectNum++
selectNum++;
}
})
});
if(selectNum > 0 && selectNum != goodsTable.data.length){
staircheckAll.value = false
isStairIndeterminate.value = true
staircheckAll.value = false;
isStairIndeterminate.value = true;
}else if(selectNum > 0 && selectNum == goodsTable.data.length){
isStairIndeterminate.value = false
isStairIndeterminate.value = false;
staircheckAll.value = true
}else{
isStairIndeterminate.value = false
staircheckAll.value = false
isStairIndeterminate.value = false;
staircheckAll.value = false;
}
}
@ -610,13 +444,13 @@ const detectionAllSelect = () => {
* 获取商品列表
*/
const loadGoodsList = (page: number = 1, callback: any = null) => {
isStairIndeterminate.value = false
staircheckAll.value = false
goodsTable.loading = true
goodsTable.data = []
isStairIndeterminate.value = false;
staircheckAll.value = false;
goodsTable.loading = true;
goodsTable.data = [];
goodsTable.page = page
const searchData = cloneDeep(goodsTable.searchParam)
const searchData = cloneDeep(goodsTable.searchParam);
if (searchData.select_type == 'selected') {
const goods_ids = <any>[]
@ -625,85 +459,79 @@ const loadGoodsList = (page: number = 1, callback: any = null) => {
}
searchData[replacePrefix+'ids'] = goods_ids
} else {
searchData[replacePrefix + 'ids'] = ''
searchData[replacePrefix+'ids'] = '';
}
getNewcomerGoodsList({
page: goodsTable.page,
limit: goodsTable.limit,
...searchData,
})
.then((res) => {
let goodsTableData = cloneDeep(res.data.data)
...searchData
}).then(res => {
let goodsTableData = cloneDeep(res.data.data);
goodsTableData.forEach((item: any) => {
item.isShow = false
item.isSecondLevelIndeterminate = false
item.secondLevelCheckAll = false
item.isShow = false;
item.isSecondLevelIndeterminate = false;
item.secondLevelCheckAll = false;
})
if (prop.mode == 'sku') {
if(prop.mode == "sku") {
goodsTableData.forEach((item: any) => {
if (item.skuList.length) {
item.skuList.forEach((skuItem: any) => {
skuItem.threeLevelCheckAll = false
skuItem.goods_type = item.goods_type
skuItem.threeLevelCheckAll = false;
skuItem.goods_type = item.goods_type;
})
}
})
}
if (callback)
callback(
prop.mode == 'spu'
? res.data.verify_goods_ids
: res.data.verify_sku_ids,
res.data.select_goods_list
)
setGoodsSelected()
if (callback) callback(prop.mode == "spu" ? res.data.verify_goods_ids : res.data.verify_sku_ids, res.data.select_goods_list)
setGoodsSelected();
goodsTable.data = goodsTableData
goodsTable.total = res.data.total
goodsTable.loading = false
})
.catch(() => {
}).catch(() => {
goodsTable.loading = false
})
}
// spu
const setGoodsSelected = () => {
nextTick(() => {
if (prop.mode == 'spu') {
if(prop.mode == "spu"){
for (let i = 0; i < goodsTable.data.length; i++) {
goodsTable.data[i].secondLevelCheckAll = false
goodsTable.data[i].secondLevelCheckAll = false;
if (selectGoods[replacePrefix + goodsTable.data[i].goods_id]) {
goodsTable.data[i].secondLevelCheckAll = true
goodsTable.data[i].secondLevelCheckAll = true;
}
}
}else{
let isAllSelectSku = true
let isAllSelectSku = true;
for (let i = 0; i < goodsTable.data.length; i++) {
goodsTable.data[i].secondLevelCheckAll = false
goodsTable.data[i].secondLevelCheckAll = false;
isAllSelectSku = true
isAllSelectSku = true;
goodsTable.data[i].isSecondLevelIndeterminate = false
goodsTable.data[i].isSecondLevelIndeterminate = false;
goodsTable.data[i].skuList.forEach((item,index) => {
item.threeLevelCheckAll = false
item.threeLevelCheckAll = false;
if (selectGoods[replacePrefix + item.sku_id]) {
goodsTable.data[i].isSecondLevelIndeterminate = true
item.threeLevelCheckAll = true
goodsTable.data[i].isSecondLevelIndeterminate = true;
item.threeLevelCheckAll = true;
}else{
isAllSelectSku = false
isAllSelectSku = false;
}
})
});
if(isAllSelectSku){
goodsTable.data[i].isSecondLevelIndeterminate = false
goodsTable.data[i].secondLevelCheckAll = true
goodsTable.data[i].isSecondLevelIndeterminate = false;
goodsTable.data[i].secondLevelCheckAll = true;
}
}
}
detectionAllSelect()
})
detectionAllSelect();
});
}
const resetForm = (formEl: FormInstance | undefined) => {
@ -715,19 +543,19 @@ const resetForm = (formEl: FormInstance | undefined) => {
const show = () => {
for (let k in selectGoods) {
delete selectGoods[k]
delete selectGoods[k];
}
replacePrefix = prop.mode == 'sku' ? 'sku_' : 'goods_'
replacePrefix = prop.mode == "sku" ? 'sku_' : 'goods_';
// idid
if (prop.mode == 'sku') {
goodsTable.searchParam.verify_sku_ids = goodsIds.value
goodsTable.searchParam.verify_sku_ids = goodsIds.value;
} else {
goodsTable.searchParam.verify_goods_ids = goodsIds.value
goodsTable.searchParam.verify_goods_ids = goodsIds.value;
}
getGoodsSkuNoPageListFn()
getGoodsSkuNoPageListFn();
loadGoodsList(1, (verify_ids: any) => {
//
@ -736,9 +564,9 @@ const show = () => {
selectGoodsId.splice(0, selectGoodsId.length, ...verify_ids)
if(Object.keys(selectGoods).length){
for(let key in selectGoods){
let num = Number(key.split(replacePrefix)[1])
let num = Number(key.split(replacePrefix)[1]);
if(goodsIds.value.indexOf(num) == -1){
delete selectGoods[key]
delete selectGoods[key];
}
}
}
@ -749,49 +577,49 @@ const show = () => {
}
const getGoodsSkuNoPageListFn = () =>{
const searchData = cloneDeep(goodsTable.searchParam)
const searchData = cloneDeep(goodsTable.searchParam);
getNewcomerSelectGoodsList({...searchData}).then((res:any)=>{
const selectGoodsData = res.data
const selectGoodsData = res.data;
//
if (prop.mode == 'sku') {
for (let i = 0; i < selectGoodsData.length; i++) {
selectGoodsData[i].skuList.forEach((item: any) => {
if (goodsIds.value.indexOf(item.sku_id) != -1) {
item.goods_name = selectGoodsData[i].goods_name //
item.goods_type_name = selectGoodsData[i].goods_type_name
item.goods_type = selectGoodsData[i].goods_type
selectGoods[replacePrefix + item.sku_id] = item
item.goods_name = selectGoodsData[i].goods_name; //
item.goods_type_name = selectGoodsData[i].goods_type_name;
item.goods_type = selectGoodsData[i].goods_type;
selectGoods[replacePrefix + item.sku_id] = item;
}
})
});
}
} else {
for (let i = 0; i < selectGoodsData.length; i++) {
if (goodsIds.value.indexOf(selectGoodsData[i].goods_id) != -1) {
selectGoods[replacePrefix + selectGoodsData[i].goods_id] =
selectGoodsData[i]
selectGoods[replacePrefix + selectGoodsData[i].goods_id] = selectGoodsData[i];
}
}
}
if(Object.keys(selectGoods).length && goodsIds.value.length){
for(let key in selectGoods){
let num = Number(key.split(replacePrefix)[1])
let num = Number(key.split(replacePrefix)[1]);
if(goodsIds.value.indexOf(num) == -1){
delete selectGoods[key]
delete selectGoods[key];
}
}
}
setGoodsSelected()
setGoodsSelected();
})
}
//
const clear = () => {
for (let k in selectGoods) {
delete selectGoods[k]
delete selectGoods[k];
}
setGoodsSelected()
setGoodsSelected();
}
const save = () => {
@ -799,72 +627,64 @@ const save = () => {
ElMessage({
type: 'warning',
message: `${t('goodsSelectPopupGoodsMinTip')}${prop.min}${t('goodsSelectPopupPiece')}`,
})
return
});
return;
}
if (
prop.max &&
prop.max > 0 &&
selectGoodsNum.value &&
selectGoodsNum.value > prop.max
) {
if (prop.max && prop.max > 0 && selectGoodsNum.value && selectGoodsNum.value > prop.max) {
ElMessage({
type: 'warning',
message: `${t('goodsSelectPopupGoodsMaxTip')}${prop.max}${t('goodsSelectPopupPiece')}`,
})
return
});
return;
}
if(prop.way == 'single'){
let realTypeNum = 0
let virtualTypeNum = 0
let realTypeNum = 0;
let virtualTypeNum = 0;
for (let k in selectGoods) {
if (selectGoods[k].goods_type == 'virtual') {
virtualTypeNum++
} else if (selectGoods[k].goods_type == 'real') {
realTypeNum++
if(selectGoods[k].goods_type == "virtual"){
virtualTypeNum++;
}else if(selectGoods[k].goods_type == "real"){
realTypeNum++;
}
}
if (
realTypeNum != Object.keys(selectGoods).length &&
virtualTypeNum != Object.keys(selectGoods).length
) {
if (realTypeNum != Object.keys(selectGoods).length && virtualTypeNum != Object.keys(selectGoods).length) {
ElMessage({
type: 'warning',
message: `${t('wayPlaceholder')}`,
})
return
});
return;
}
}
let ids: any = []
let ids: any = [];
for (let k in selectGoods) {
ids.push(parseInt(k.replace(replacePrefix, '')))
ids.push(parseInt(k.replace(replacePrefix, '')));
}
goodsIds.value.splice(0, goodsIds.value.length, ...ids)
emit('goodsSelect',selectGoods)
initSearchParam()
initSearchParam();
showDialog.value = false
}
//
const initSearchParam = ()=>{
goodsTable.searchParam.keyword = ''
goodsTable.searchParam.goods_category = []
goodsTable.searchParam.select_type = 'all'
goodsTable.searchParam.goods_ids = ''
goodsTable.searchParam.verify_goods_ids = ''
goodsTable.searchParam.verify_sku_ids = ''
goodsTable.searchParam.goods_type = ''
goodsTable.searchParam.keyword = '';
goodsTable.searchParam.goods_category = [];
goodsTable.searchParam.select_type = 'all';
goodsTable.searchParam.goods_ids = '';
goodsTable.searchParam.verify_goods_ids = '';
goodsTable.searchParam.verify_sku_ids = '';
goodsTable.searchParam.goods_type = '';
}
defineExpose({
showDialog,
selectGoods,
selectGoodsNum,
selectGoodsNum
})
</script>

58
admin/src/addon/shop/views/goods/components/service-edit.vue

@ -1,55 +1,21 @@
<template>
<el-dialog
v-model="showDialog"
:title="title"
width="500px"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
v-loading="loading"
>
<el-dialog v-model="showDialog" :title="title" width="500px" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form" v-loading="loading">
<el-form-item :label="t('serviceName')" prop="service_name">
<el-input
v-model.trim="formData.service_name"
show-word-limit
clearable
:placeholder="t('serviceNamePlaceholder')"
:maxlength="20"
class="input-width"
/>
<el-input v-model.trim="formData.service_name" show-word-limit clearable :placeholder="t('serviceNamePlaceholder')" :maxlength="20" class="input-width" />
</el-form-item>
<el-form-item :label="t('image')">
<upload-image v-model="formData.image" />
</el-form-item>
<el-form-item :label="t('desc')" >
<el-input
v-model.trim="formData.desc"
type="textarea"
rows="5"
show-word-limit
clearable
:placeholder="t('descPlaceholder')"
:maxlength="200"
class="input-width"
/>
<el-input v-model.trim="formData.desc" type="textarea" rows="5" show-word-limit clearable :placeholder="t('descPlaceholder')" :maxlength="200" class="input-width"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button
type="primary"
:loading="loading"
@click="confirm(formRef)"
>{{ t('confirm') }}</el-button
>
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -72,7 +38,7 @@ const initialFormData = {
service_id: '',
service_name: '',
image: '',
desc: '',
desc: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -82,8 +48,8 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
service_name: [
{ required: true, message: t('serviceNamePlaceholder'), trigger: 'blur' },
],
{ required: true, message: t('serviceNamePlaceholder'), trigger: 'blur' }
]
}
})
@ -103,13 +69,11 @@ const confirm = async (formEl: FormInstance | undefined) => {
const data = formData
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
showDialog.value = false
emit('complete')
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
@ -136,7 +100,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>

271
admin/src/addon/shop/views/goods/evaluate.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="addEvent">
@ -8,40 +9,20 @@
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="evaluateTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="evaluateTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('goodsName')" prop="goods_name">
<el-input
v-model.trim="evaluateTable.searchParam.goods_name"
:placeholder="t('goodsNamePlaceholder')"
class="input-width"
maxlength="60"
/>
<el-input v-model.trim="evaluateTable.searchParam.goods_name" :placeholder="t('goodsNamePlaceholder')" class="input-width" maxlength="60" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadEvaluateList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadEvaluateList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="evaluateTable.data"
size="large"
v-loading="evaluateTable.loading"
>
<el-table :data="evaluateTable.data" size="large" v-loading="evaluateTable.loading">
<template #empty>
<span>{{ !evaluateTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -49,28 +30,15 @@
<template #default="{ row }">
<div class="flex cursor-pointer">
<div class="flex items-center min-w-[50px] mr-[10px]">
<el-image
v-if="row.goods.goods_cover_thumb_small"
class="w-[50px] h-[50px]"
:src="img(row.goods.goods_cover_thumb_small)"
fit="contain"
>
<el-image v-if="row.goods.goods_cover_thumb_small" class="w-[50px] h-[50px]" :src="img(row.goods.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[50px] h-[50px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[50px] h-[50px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[50px] h-[50px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[50px] h-[50px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="flex">
<p class="multi-hidden">{{ row.goods.goods_name }}</p>
@ -82,45 +50,19 @@
<template #default="{ row }">
<div>
<p class="text-[14px]">{{ row.content }}</p>
<div
class="flex flex-wrap mt-[10px]"
v-if="row.images?.length > 0"
>
<div
v-for="(imageItem, imageIndex) in row.images"
:key="imageIndex"
class="mr-4"
>
<el-image
v-if="imageItem"
class="w-[40px] h-[40px]"
:src="img(imageItem)"
fit="contain"
:preview-src-list="[img(imageItem)]"
:zoom-rate="1.2"
:max-scale="7"
:min-scale="0.2"
>
<div class="flex flex-wrap mt-[10px]" v-if="row.images?.length > 0">
<div v-for="(imageItem, imageIndex) in row.images" :key="imageIndex" class="mr-4">
<el-image v-if="imageItem" class="w-[40px] h-[40px]" :src="img(imageItem)" fit="contain" :preview-src-list="[img(imageItem)]" :zoom-rate="1.2" :max-scale="7" :min-scale="0.2">
<template #error>
<div class="image-slot">
<img
class="w-[40px] h-[40px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[40px] h-[40px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[40px] h-[40px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img v-else class="w-[40px] h-[40px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</div>
<p class="mt-[15px] text-[14px]" v-if="row.explain_first">
<span class="text-[#ff7f5b]">{{ t('explainFirst') }}</span
>{{ row.explain_first }}
</p>
<p class="mt-[15px] text-[14px]" v-if="row.explain_first"><span class="text-[#ff7f5b]">{{ t('explainFirst') }}</span>{{ row.explain_first }}</p>
</div>
</template>
</el-table-column>
@ -129,119 +71,43 @@
<el-rate v-model="row.scores" disabled />
</template>
</el-table-column>
<el-table-column
prop="audit_name"
:label="t('auditName')"
min-width="80"
/>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="120"
/>
<el-table-column
:label="t('operation')"
fixed="right"
min-width="100"
align="right"
>
<el-table-column prop="audit_name" :label="t('auditName')" min-width="80" />
<el-table-column prop="create_time" :label="t('createTime')" min-width="120" />
<el-table-column :label="t('operation')" fixed="right" min-width="100" align="right">
<template #default="{ row }">
<div>
<el-button
type="primary"
link
@click="adoptEvent(row.evaluate_id)"
v-if="row.is_audit == 1"
>{{ t('adopt') }}</el-button
>
<el-button
type="primary"
link
@click="refuseEvent(row.evaluate_id)"
v-if="row.is_audit == 1"
>{{ t('refuse') }}</el-button
>
<el-button
type="primary"
link
@click="replyEvent(row.evaluate_id)"
v-if="row.explain_first == ''"
>{{ t('reply') }}</el-button
>
<el-button
type="primary"
link
@click="deleteEvent(row.evaluate_id)"
>{{ t('delete') }}</el-button
>
<el-button
type="primary"
link
@click="toppingEvent(row.evaluate_id, 'topping')"
v-if="row.is_audit == 2 && row.topping == 0"
>{{ t('topping') }}</el-button
>
<el-button
type="primary"
link
@click="toppingEvent(row.evaluate_id, 'cancel_topping')"
v-if="row.topping == 1"
>{{ t('cancelTopping') }}</el-button
>
<el-button type="primary" link @click="adoptEvent(row.evaluate_id)" v-if="row.is_audit == 1">{{ t('adopt') }}</el-button>
<el-button type="primary" link @click="refuseEvent(row.evaluate_id)" v-if="row.is_audit == 1">{{ t('refuse') }}</el-button>
<el-button type="primary" link @click="replyEvent(row.evaluate_id)" v-if="row.explain_first == ''">{{ t('reply') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.evaluate_id)">{{ t('delete') }}</el-button>
<el-button type="primary" link @click="toppingEvent(row.evaluate_id, 'topping')" v-if="row.is_audit == 2 && row.topping == 0">{{ t('topping') }}</el-button>
<el-button type="primary" link @click="toppingEvent(row.evaluate_id, 'cancel_topping')" v-if="row.topping == 1">{{ t('cancelTopping') }}</el-button>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="evaluateTable.page"
v-model:page-size="evaluateTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="evaluateTable.total"
@size-change="loadEvaluateList()"
@current-change="loadEvaluateList"
/>
<el-pagination v-model:current-page="evaluateTable.page" v-model:page-size="evaluateTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="evaluateTable.total"
@size-change="loadEvaluateList()" @current-change="loadEvaluateList" />
</div>
</div>
</el-card>
<evaluate-add ref="editEvaluateDialog" @complete="loadEvaluateList" />
<el-dialog
v-model="replyShowDialog"
:title="t('explainFirst')"
width="460px"
class="diy-dialog-wrap"
:destroy-on-close="true"
>
<el-form
:model="formData"
label-width="90px"
ref="formRef"
:rules="formRules"
class="page-form"
>
<el-dialog v-model="replyShowDialog" :title="t('explainFirst')" width="460px" class="diy-dialog-wrap" :destroy-on-close="true">
<el-form :model="formData" label-width="90px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('explainFirst')" prop="explain_first">
<el-input
v-model.trim="formData.explain_first"
type="textarea"
rows="4"
clearable
:placeholder="t('explainFirstPlaceholder')"
class="input-width"
maxlength="200"
show-word-limit
/>
<el-input v-model.trim="formData.explain_first" type="textarea" rows="4" clearable
:placeholder="t('explainFirstPlaceholder')" class="input-width" maxlength="200" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="replyShowDialog = false">{{
t('cancel')
}}</el-button>
<el-button type="primary" @click="confirm(formRef)">{{
t('confirm')
}}</el-button>
<el-button @click="replyShowDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
</span>
</template>
</el-dialog>
@ -251,15 +117,7 @@
<script lang="ts" setup>
import { reactive, ref, computed } from 'vue'
import { t } from '@/lang'
import {
getEvaluateList,
deleteEvaluate,
adoptEvaluate,
refuseEvaluate,
replyEvaluate,
toppingEvaluate,
cancelToppingEvaluate,
} from '@/addon/shop/api/goods'
import { getEvaluateList, deleteEvaluate, adoptEvaluate, refuseEvaluate, replyEvaluate, toppingEvaluate, cancelToppingEvaluate } from '@/addon/shop/api/goods'
import EvaluateAdd from '@/addon/shop/views/goods/components/evaluate-add.vue'
import { img } from '@/utils/common'
import { ElMessageBox, FormInstance } from 'element-plus'
@ -275,8 +133,8 @@ const evaluateTable = reactive({
loading: true,
data: [],
searchParam: {
goods_name: '',
},
goods_name: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -291,9 +149,8 @@ const loadEvaluateList = (page: number = 1) => {
getEvaluateList({
page: evaluateTable.page,
limit: evaluateTable.limit,
...evaluateTable.searchParam,
})
.then((res) => {
...evaluateTable.searchParam
}).then(res => {
evaluateTable.loading = false
evaluateTable.data = res.data.data
evaluateTable.total = res.data.total
@ -303,8 +160,7 @@ const loadEvaluateList = (page: number = 1) => {
})
return item
})
})
.catch(() => {
}).catch(() => {
evaluateTable.loading = false
})
}
@ -323,31 +179,33 @@ const addEvent = () => {
* 删除商品评价
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('evaluateDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('evaluateDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteEvaluate(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteEvaluate(id).then(() => {
loadEvaluateList()
}).catch(() => {
})
.catch(() => {})
})
}
//
const adoptEvent = (id: number) => {
ElMessageBox.confirm(t('auditAdoptTips'), t('warning'), {
ElMessageBox.confirm(t('auditAdoptTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
adoptEvaluate(id)
.then(() => {
type: 'warning'
}
).then(() => {
adoptEvaluate(id).then(() => {
loadEvaluateList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -375,7 +233,7 @@ const toppingEvent = (id: number, event: string) => {
const replyShowDialog = ref(false)
const initialFormData = {
evaluate_id: 0,
explain_first: '',
explain_first: ''
}
const formData: Record<string, any> = reactive({ ...initialFormData })
const formRef = ref<FormInstance>()
@ -387,12 +245,8 @@ const replyEvent = (id: number) => {
const formRules = computed(() => {
return {
explain_first: [
{
required: true,
message: t('explainFirstPlaceholder'),
trigger: 'blur',
},
],
{ required: true, message: t('explainFirstPlaceholder'), trigger: 'blur' }
]
}
})
const confirm = async (formEl: FormInstance | undefined) => {
@ -400,12 +254,10 @@ const confirm = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => {
if (valid) {
const data = formData
replyEvaluate(data)
.then((res) => {
replyEvaluate(data).then(res => {
loadEvaluateList()
replyShowDialog.value = false
})
.catch((err) => {
}).catch(err => {
replyShowDialog.value = false
})
}
@ -428,5 +280,4 @@ const resetForm = (formEl: FormInstance | undefined) => {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>
}</style>

133
admin/src/addon/shop/views/goods/label_group_list.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{pageName}}</span>
<el-button type="primary" @click="addEvent">
@ -10,105 +11,50 @@
<el-tabs model-value="/shop/goods/label/group" @tab-change="handleClick">
<el-tab-pane :label="t('tabGoodsLabel')" name="/shop/goods/label" />
<el-tab-pane
:label="t('tabGoodsLabelGroup')"
name="/shop/goods/label/group"
/>
<el-tab-pane :label="t('tabGoodsLabelGroup')" name="/shop/goods/label/group" />
</el-tabs>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="labelTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="labelTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('groupName')" prop="group_name">
<el-input
v-model.trim="labelTable.searchParam.group_name"
:placeholder="t('groupNamePlaceholder')"
/>
<el-input v-model.trim="labelTable.searchParam.group_name" :placeholder="t('groupNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadLabelGroupList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadLabelGroupList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="labelTable.data"
size="large"
v-loading="labelTable.loading"
@sort-change="sortChange"
>
<el-table :data="labelTable.data" size="large" v-loading="labelTable.loading" @sort-change="sortChange">
<template #empty>
<span>{{ !labelTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="group_name"
:label="t('groupName')"
min-width="120"
/>
<el-table-column
prop="sort"
:label="t('sort')"
min-width="120"
sortable="custom"
>
<el-table-column prop="group_name" :label="t('groupName')" min-width="120" />
<el-table-column prop="sort" :label="t('sort')" min-width="120" sortable="custom">
<template #default="{ row }">
<el-input
v-model.trim="row.sort"
class="!w-[100px]"
maxlength="8"
@blur="sortInputListener(row.sort, row)"
/>
<el-input v-model.trim="row.sort" class="!w-[100px]" maxlength="8" @blur="sortInputListener(row.sort, row)" />
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.group_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.group_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="labelTable.page"
v-model:page-size="labelTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="labelTable.total"
@size-change="loadLabelGroupList()"
@current-change="loadLabelGroupList"
/>
<el-pagination v-model:current-page="labelTable.page" v-model:page-size="labelTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="labelTable.total"
@size-change="loadLabelGroupList()" @current-change="loadLabelGroupList" />
</div>
</div>
<label-group-edit
ref="editLabelGroupDialog"
@complete="loadLabelGroupList"
/>
<label-group-edit ref="editLabelGroupDialog" @complete="loadLabelGroupList" />
</el-card>
</div>
</template>
@ -116,11 +62,7 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getLabelGroupPageList,
deleteLabelGroup,
modifyLabelGroupSort,
} from '@/addon/shop/api/goods'
import { getLabelGroupPageList, deleteLabelGroup, modifyLabelGroupSort } from '@/addon/shop/api/goods'
import { ElMessageBox, FormInstance, ElMessage } from 'element-plus'
import LabelGroupEdit from '@/addon/shop/views/goods/components/label-group-edit.vue'
import { useRoute,useRouter } from 'vue-router'
@ -143,8 +85,8 @@ const labelTable = reactive({
searchParam: {
group_name: '',
order: '',
sort: '',
},
sort: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -174,14 +116,12 @@ const loadLabelGroupList = (page: number = 1) => {
getLabelGroupPageList({
page: labelTable.page,
limit: labelTable.limit,
...labelTable.searchParam,
})
.then((res) => {
...labelTable.searchParam
}).then(res => {
labelTable.loading = false
labelTable.data = res.data.data
labelTable.total = res.data.total
})
.catch(() => {
}).catch(() => {
labelTable.loading = false
})
}
@ -210,16 +150,17 @@ const editEvent = (data: any) => {
* 删除商品标签
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('labelGroupDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('labelGroupDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteLabelGroup(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteLabelGroup(id).then(() => {
loadLabelGroupList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -228,7 +169,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !/^\d{0,8}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${t('sortTips')}`,
message: `${ t('sortTips') }`
})
return
}
@ -237,8 +178,9 @@ const sortInputListener = debounce((sort, row) => {
}
modifyLabelGroupSort({
group_id: row.group_id,
sort,
}).then((res) => {})
sort
}).then((res) => {
})
})
const resetForm = (formEl: FormInstance | undefined) => {
@ -248,4 +190,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

211
admin/src/addon/shop/views/goods/label_list.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{pageName}}</span>
<el-button type="primary" @click="addEvent">
@ -10,186 +11,82 @@
<el-tabs model-value="/shop/goods/label" @tab-change="handleClick">
<el-tab-pane :label="t('tabGoodsLabel')" name="/shop/goods/label" />
<el-tab-pane
:label="t('tabGoodsLabelGroup')"
name="/shop/goods/label/group"
/>
<el-tab-pane :label="t('tabGoodsLabelGroup')" name="/shop/goods/label/group" />
</el-tabs>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="labelTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="labelTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('labelName')" prop="label_name">
<el-input
v-model.trim="labelTable.searchParam.label_name"
:placeholder="t('labelNamePlaceholder')"
/>
<el-input v-model.trim="labelTable.searchParam.label_name" :placeholder="t('labelNamePlaceholder')" />
</el-form-item>
<el-form-item :label="t('groupName')" prop="group_id">
<el-select
v-model="labelTable.searchParam.group_id"
:placeholder="t('groupNamePlaceholder')"
clearable
>
<el-option
v-for="item in groupList"
:key="item.group_id"
:label="item.group_name"
:value="item.group_id"
/>
<el-select v-model="labelTable.searchParam.group_id" :placeholder="t('groupNamePlaceholder')" clearable>
<el-option v-for="item in groupList" :key="item.group_id" :label="item.group_name" :value="item.group_id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadLabelList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadLabelList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="labelTable.data"
size="large"
v-loading="labelTable.loading"
@sort-change="sortChange"
>
<el-table :data="labelTable.data" size="large" v-loading="labelTable.loading" @sort-change="sortChange">
<template #empty>
<span>{{ !labelTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="label_name"
:label="t('label')"
min-width="120"
>
<el-table-column prop="label_name" :label="t('label')" min-width="120">
<template #default="{ row }">
<div
v-if="row.style_type == 'diy'"
class="inline-block px-[10px] text-[12px] rounded-[4px] box-border whitespace-nowrap h-[28px] leading-[28px]"
:style="{
color: row.color_json.text_color,
backgroundColor: row.color_json.bg_color,
border: row.color_json.border_color
? '1px solid ' + row.color_json.border_color
: 'none',
}"
>
<div v-if="row.style_type == 'diy'" class="inline-block px-[10px] text-[12px] rounded-[4px] box-border whitespace-nowrap h-[28px] leading-[28px]"
:style="{ color : row.color_json.text_color, backgroundColor : row.color_json.bg_color, border : row.color_json.border_color ? '1px solid ' + row.color_json.border_color : 'none' }">
<span>{{ row.label_name }}</span>
</div>
<img
v-else-if="row.style_type == 'icon'"
class="block h-[28px] rounded-[4px] object-cover"
:src="img(row.icon)"
/>
<img v-else-if="row.style_type == 'icon'" class="block h-[28px] rounded-[4px] object-cover" :src="img(row.icon)" />
</template>
</el-table-column>
<el-table-column
prop="group_name"
:label="t('groupName')"
min-width="120"
>
<el-table-column prop="group_name" :label="t('groupName')" min-width="120">
<template #default="{ row }">
<span v-if="row.group">{{ row.group.group_name }}</span>
<span v-else>--</span>
</template>
</el-table-column>
<el-table-column
prop="status"
:label="t('status')"
min-width="80"
:show-overflow-tooltip="true"
>
<el-table-column prop="status" :label="t('status')" min-width="80" :show-overflow-tooltip="true" >
<template #default="{ row }">
<el-tag
type="success"
v-if="row.status == 1"
@click="modifyLabelStatusEvent(row.label_id, 0)"
class="cursor-pointer"
>{{ t('statusOn') }}</el-tag
>
<el-tag
type="info"
v-else
@click="modifyLabelStatusEvent(row.label_id, 1)"
class="cursor-pointer"
>{{ t('statusOff') }}</el-tag
>
<el-tag type="success" v-if="row.status == 1" @click="modifyLabelStatusEvent(row.label_id, 0)" class="cursor-pointer">{{ t('statusOn') }}</el-tag>
<el-tag type="info" v-else @click="modifyLabelStatusEvent(row.label_id, 1)" class="cursor-pointer">{{ t('statusOff') }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="memo" :label="t('memo')" min-width="200" />
<el-table-column
prop="sort"
:label="t('sort')"
min-width="120"
sortable="custom"
>
<el-table-column prop="sort" :label="t('sort')" min-width="120" sortable="custom">
<template #default="{ row }">
<el-input
v-model.trim="row.sort"
class="!w-[100px]"
maxlength="8"
@blur="sortInputListener(row.sort, row)"
/>
<el-input v-model.trim="row.sort" class="!w-[100px]" maxlength="8" @blur="sortInputListener(row.sort, row)" />
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="100"
sortable="custom"
>
<el-table-column prop="create_time" :label="t('createTime')" min-width="100" sortable="custom">
<template #default="{ row }">
<div>{{ row.create_time }}</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.label_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.label_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="labelTable.page"
v-model:page-size="labelTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="labelTable.total"
@size-change="loadLabelList()"
@current-change="loadLabelList"
/>
<el-pagination v-model:current-page="labelTable.page" v-model:page-size="labelTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="labelTable.total"
@size-change="loadLabelList()" @current-change="loadLabelList" />
</div>
</div>
<label-edit
ref="editLabelDialog"
@complete="loadLabelList"
:groupList="groupList"
/>
<label-edit ref="editLabelDialog" @complete="loadLabelList" :groupList="groupList" />
</el-card>
</div>
</template>
@ -197,13 +94,7 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getLabelPageList,
deleteLabel,
modifyLabelSort,
getLabelGroupList,
modifyLabelStatus,
} from '@/addon/shop/api/goods'
import { getLabelPageList, deleteLabel, modifyLabelSort,getLabelGroupList,modifyLabelStatus } from '@/addon/shop/api/goods'
import { ElMessageBox, FormInstance, ElMessage } from 'element-plus'
import LabelEdit from '@/addon/shop/views/goods/components/label-edit.vue'
import { useRoute,useRouter } from 'vue-router'
@ -227,11 +118,11 @@ const labelTable = reactive({
label_name: '',
group_id: '',
order: '',
sort: '',
},
sort: ''
}
})
const groupList: any = reactive([])
const groupList: any = reactive([]);
const searchFormRef = ref<FormInstance>()
@ -260,14 +151,12 @@ const loadLabelList = (page: number = 1) => {
getLabelPageList({
page: labelTable.page,
limit: labelTable.limit,
...labelTable.searchParam,
})
.then((res) => {
...labelTable.searchParam
}).then(res => {
labelTable.loading = false
labelTable.data = res.data.data
labelTable.total = res.data.total
})
.catch(() => {
}).catch(() => {
labelTable.loading = false
})
}
@ -282,7 +171,7 @@ const initData = () => {
loadLabelList()
}
initData()
initData();
const editLabelDialog: Record<string, any> | null = ref(null)
@ -307,16 +196,17 @@ const editEvent = (data: any) => {
* 删除商品标签
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('labelDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('labelDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteLabel(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteLabel(id).then(() => {
loadLabelList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -325,7 +215,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !/^\d{0,8}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${t('sortTips')}`,
message: `${ t('sortTips') }`
})
return
}
@ -334,9 +224,11 @@ const sortInputListener = debounce((sort, row) => {
}
modifyLabelSort({
label_id: row.label_id,
sort,
}).then((res) => {})
sort
}).then((res) => {
})
})
const isRepeat = ref(false)
@ -347,7 +239,7 @@ const modifyLabelStatusEvent = (label_id: any, status: any) => {
modifyLabelStatus({
label_id,
status,
status
}).then((res) => {
loadLabelList()
isRepeat.value = false
@ -361,4 +253,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

532
admin/src/addon/shop/views/goods/list.vue

@ -1,257 +1,106 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="addEvent">{{
t('addGoods')
}}</el-button>
<el-button type="primary" @click="addEvent">{{ t('addGoods') }}</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="goodsTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="goodsTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('goodsName')" prop="goods_name">
<el-input
v-model.trim="goodsTable.searchParam.goods_name"
:placeholder="t('goodsNamePlaceholder')"
maxlength="60"
/>
<el-input v-model.trim="goodsTable.searchParam.goods_name" :placeholder="t('goodsNamePlaceholder')" maxlength="60" />
</el-form-item>
<el-form-item :label="t('goodsCategory')" prop="goods_category">
<!-- <el-cascader v-model="goodsTable.searchParam.goods_category" :options="goodsCategoryOptions" :placeholder="t('goodsCategoryPlaceholder')" clearable :props="{ value: 'value', label: 'label', emitPath:false }"/> -->
<el-cascader
v-model="goodsTable.searchParam.goods_category"
ref="cascader"
:options="goodsCategoryOptions"
@change="handleCascaderChange"
:placeholder="t('goodsCategoryPlaceholder')"
clearable
:props="{
value: 'value',
label: 'label',
emitPath: false,
multiple: false,
checkStrictly: true,
expandTrigger: 'hover',
}"
/>
<el-cascader v-model="goodsTable.searchParam.goods_category" ref="cascader" :options="goodsCategoryOptions" @change="handleCascaderChange" :placeholder="t('goodsCategoryPlaceholder')" clearable :props="{ value: 'value', label: 'label', emitPath:false, multiple: false,checkStrictly: true,expandTrigger: 'hover'}"/>
</el-form-item>
<el-form-item :label="t('goodsType')" prop="goods_type">
<el-select
v-model="goodsTable.searchParam.goods_type"
:placeholder="t('goodsTypePlaceholder')"
clearable
>
<el-option
v-for="item in goodsType"
:key="item.type"
:label="item.name"
:value="item.type"
/>
<el-select v-model="goodsTable.searchParam.goods_type" :placeholder="t('goodsTypePlaceholder')" clearable>
<el-option v-for="item in goodsType" :key="item.type" :label="item.name" :value="item.type" />
</el-select>
</el-form-item>
<el-form-item :label="t('brand')" prop="brand_id">
<el-select
v-model="goodsTable.searchParam.brand_id"
:placeholder="t('brandPlaceholder')"
clearable
>
<el-option
v-for="item in brandOptions"
:key="item.brand_id"
:label="item.brand_name"
:value="item.brand_id"
/>
<el-select v-model="goodsTable.searchParam.brand_id" :placeholder="t('brandPlaceholder')" clearable>
<el-option v-for="item in brandOptions" :key="item.brand_id" :label="item.brand_name" :value="item.brand_id" />
</el-select>
</el-form-item>
<el-form-item :label="t('labelIds')" prop="label_ids">
<el-select
v-model="goodsTable.searchParam.label_ids"
:placeholder="t('labelIdsPlaceholder')"
clearable
>
<el-option
v-for="item in labelOptions"
:key="item.label_id"
:label="item.label_name"
:value="item.label_id"
/>
<el-select v-model="goodsTable.searchParam.label_ids" :placeholder="t('labelIdsPlaceholder')" clearable>
<el-option v-for="item in labelOptions" :key="item.label_id" :label="item.label_name" :value="item.label_id" />
</el-select>
</el-form-item>
<el-form-item :label="t('saleNum')" prop="sale_num">
<div class="region-input">
<input
type="text"
:placeholder="t('startSaleNumPlaceholder')"
maxlength="10"
v-model.trim="goodsTable.searchParam.start_sale_num"
@keyup="filterDigit($event)"
/>
<input type="text" :placeholder="t('startSaleNumPlaceholder')" maxlength="10" v-model.trim="goodsTable.searchParam.start_sale_num" @keyup="filterDigit($event)">
<span class="separator">-</span>
<input
type="text"
:placeholder="t('endSaleNumPlaceholder')"
maxlength="10"
v-model.trim="goodsTable.searchParam.end_sale_num"
@keyup="filterDigit($event)"
/>
<input type="text" :placeholder="t('endSaleNumPlaceholder')" maxlength="10" v-model.trim="goodsTable.searchParam.end_sale_num" @keyup="filterDigit($event)">
</div>
</el-form-item>
<el-form-item :label="t('skuPrice')" prop="sku_price">
<div class="region-input">
<input
type="text"
:placeholder="t('startPricePlaceholder')"
maxlength="10"
v-model.trim="goodsTable.searchParam.start_price"
@keyup="filterDigit($event)"
/>
<input type="text" :placeholder="t('startPricePlaceholder')" maxlength="10" v-model.trim="goodsTable.searchParam.start_price" @keyup="filterDigit($event)">
<span class="separator">-</span>
<input
type="text"
:placeholder="t('endPricePlaceholder')"
maxlength="10"
v-model.trim="goodsTable.searchParam.end_price"
@keyup="filterDigit($event)"
/>
<input type="text" :placeholder="t('endPricePlaceholder')" maxlength="10" v-model.trim="goodsTable.searchParam.end_price" @keyup="filterDigit($event)">
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadGoodsList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadGoodsList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-tabs
v-model="goodsTable.searchParam.status"
class="goods-tabs"
@tab-click="tabHandleClick"
>
<el-tabs v-model="goodsTable.searchParam.status" class="goods-tabs" @tab-click="tabHandleClick">
<el-tab-pane :label="t('statusOn')" name="1"></el-tab-pane>
<el-tab-pane :label="t('statusOff')" name="0"></el-tab-pane>
<el-tab-pane :label="t('statusAll')" name=""></el-tab-pane>
</el-tabs>
<div class="mb-[10px] flex items-center">
<el-checkbox
v-model="toggleCheckbox"
size="large"
class="px-[14px]"
@change="toggleChange"
:indeterminate="isIndeterminate"
/>
<el-button
@click="batchGoodsStatus(1)"
size="small"
v-if="goodsTable.searchParam.status != '1'"
>{{ t('batchOnGoods') }}</el-button
>
<el-button
@click="batchGoodsStatus(0)"
size="small"
v-if="goodsTable.searchParam.status != '0'"
>{{ t('batchOffGoods') }}</el-button
>
<el-button @click="batchDeleteGoods" size="small">{{
t('batchDeleteGoods')
}}</el-button>
<el-button @click="batchSetGoods" size="small">{{
t('batchSetting')
}}</el-button>
<el-checkbox v-model="toggleCheckbox" size="large" class="px-[14px]" @change="toggleChange" :indeterminate="isIndeterminate" />
<el-button @click="batchGoodsStatus(1)" size="small" v-if="goodsTable.searchParam.status != '1'">{{ t('batchOnGoods') }}</el-button>
<el-button @click="batchGoodsStatus(0)" size="small" v-if="goodsTable.searchParam.status != '0'">{{ t('batchOffGoods') }}</el-button>
<el-button @click="batchDeleteGoods" size="small">{{ t('batchDeleteGoods') }}</el-button>
<el-button @click="batchSetGoods" size="small">{{ t('batchSetting') }}</el-button>
</div>
<el-table
:data="goodsTable.data"
size="large"
v-loading="goodsTable.loading"
ref="goodsListTableRef"
@sort-change="sortChange"
@selection-change="handleSelectionChange"
>
<el-table :data="goodsTable.data" size="large" v-loading="goodsTable.loading" ref="goodsListTableRef" @sort-change="sortChange" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ !goodsTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
prop="goods_id"
:label="t('goodsInfo')"
min-width="300"
>
<el-table-column prop="goods_id" :label="t('goodsInfo')" min-width="300">
<template #default="{ row }">
<div
class="flex items-center cursor-pointer"
@click="previewEvent(row)"
>
<div
class="min-w-[70px] h-[70px] flex items-center justify-center"
>
<el-image
v-if="row.goods_cover_thumb_small"
class="w-[70px] h-[70px]"
:src="img(row.goods_cover_thumb_small)"
fit="contain"
>
<div class="flex items-center cursor-pointer" @click="previewEvent(row)">
<div class="min-w-[70px] h-[70px] flex items-center justify-center">
<el-image v-if="row.goods_cover_thumb_small" class="w-[70px] h-[70px]" :src="img(row.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2 flex flex-col items-start">
<span :title="row.goods_name" class="multi-hidden">{{
row.goods_name
}}</span>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
<span
class="px-[4px] text-[12px] text-[#fff] rounded-[4px] bg-primary leading-[18px]"
v-if="row.is_gift == 1"
>赠品</span
>
<span :title="row.goods_name" class="multi-hidden">{{ row.goods_name }}</span>
<span class="text-primary text-[12px]">{{ row.goods_type_name }}</span>
<span class="px-[4px] text-[12px] text-[#fff] rounded-[4px] bg-primary leading-[18px]" v-if="row.is_gift == 1">赠品</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column
prop="price"
:label="t('skuPrice')"
min-width="120"
align="right"
sortable="custom"
>
<el-table-column prop="price" :label="t('skuPrice')" min-width="120" align="right" sortable="custom">
<template #default="{ row }">
<div
class="cursor-pointer price-wrap"
@click="editPriceEvent(row)"
>
<div class="cursor-pointer price-wrap" @click="editPriceEvent(row)">
<span>{{ row.goodsSku.price }}</span>
<el-icon class="icon-wrap ml-[5px] invisible">
<EditPen />
@ -260,17 +109,9 @@
</template>
</el-table-column>
<el-table-column
prop="stock"
:label="t('stock')"
min-width="120"
sortable="custom"
>
<el-table-column prop="stock" :label="t('stock')" min-width="120" sortable="custom">
<template #default="{ row }">
<div
class="cursor-pointer stock-wrap"
@click="editStockEvent(row)"
>
<div class="cursor-pointer stock-wrap" @click="editStockEvent(row)">
<span>{{ row.stock }}</span>
<el-icon class="icon-wrap ml-[5px] invisible">
<EditPen />
@ -278,87 +119,37 @@
</div>
</template>
</el-table-column>
<el-table-column
prop="sale_num"
:label="t('saleNum')"
min-width="100"
sortable="custom"
/>
<el-table-column prop="sale_num" :label="t('saleNum')" min-width="100" sortable="custom" />
<el-table-column prop="status" :label="t('status')" min-width="100">
<template #default="{ row }">
<div v-if="row.status == 1">{{ t('statusOn') }}</div>
<div v-if="row.status == 0">{{ t('statusOff') }}</div>
</template>
</el-table-column>
<el-table-column
prop="sort"
:label="t('sort')"
min-width="120"
sortable="custom"
>
<el-table-column prop="sort" :label="t('sort')" min-width="120" sortable="custom">
<template #default="{ row }">
<el-input
v-model.trim="row.sort"
class="w-[70px]"
maxlength="8"
@blur="sortInputListener(row.sort, row)"
/>
<el-input v-model.trim="row.sort" class="w-[70px]" maxlength="8" @blur="sortInputListener(row.sort, row)" />
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="150"
sortable="custom"
>
<el-table-column prop="create_time" :label="t('createTime')" min-width="150" sortable="custom">
<template #default="{ row }">
<div>{{ row.create_time }}</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="spreadEvent(row)">{{
t('spreadGoods')
}}</el-button>
<el-button type="primary" link @click="memberPriceEvent(row)">{{
t('memberPrice')
}}</el-button>
<el-button
type="primary"
v-if="row.status == 1"
link
@click="statusChange(row, 0)"
>{{ t('statusActionOff') }}</el-button
>
<el-button
type="primary"
v-else
link
@click="statusChange(row, 1)"
>{{ t('statusActionOn') }}</el-button
>
<el-button type="primary" link @click="copyEvent(row)">{{
t('copyGoods')
}}</el-button>
<el-button
type="primary"
v-if="row.status != 1"
link
@click="deleteEvent(row.goods_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="spreadEvent(row)">{{ t('spreadGoods') }}</el-button>
<el-button type="primary" link @click="memberPriceEvent(row)">{{ t('memberPrice') }}</el-button>
<el-button type="primary" v-if="row.status == 1" link @click="statusChange(row, 0)">{{ t('statusActionOff') }}</el-button>
<el-button type="primary" v-else link @click="statusChange(row, 1)">{{ t('statusActionOn') }}</el-button>
<el-button type="primary" link @click="copyEvent(row)">{{ t('copyGoods') }}</el-button>
<el-button type="primary" v-if="row.status != 1" link @click="deleteEvent(row.goods_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<!-- <div class="flex items-center flex-1">
@ -368,29 +159,20 @@
<el-button @click="batchDeleteGoods" size="small">{{ t('batchDeleteGoods') }}</el-button>
</div> -->
<el-pagination
v-model:current-page="goodsTable.page"
v-model:page-size="goodsTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="goodsTable.total"
@size-change="loadGoodsList()"
@current-change="loadGoodsList"
/>
<el-pagination v-model:current-page="goodsTable.page" v-model:page-size="goodsTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="goodsTable.total"
@size-change="loadGoodsList()" @current-change="loadGoodsList" />
</div>
</div>
</el-card>
<!-- 商品库存编辑弹出框 -->
<goods-stock-edit-popup
ref="goodsStockEditPopupRef"
@load="loadGoodsList"
/>
<goods-stock-edit-popup ref="goodsStockEditPopupRef" @load="loadGoodsList" />
<!-- 商品价格编辑弹出框 -->
<goods-price-edit-popup
ref="goodsPriceEditPopupRef"
@load="loadGoodsList"
/>
<goods-price-edit-popup ref="goodsPriceEditPopupRef" @load="loadGoodsList" />
<!-- 商品推广弹出框 -->
<goods-spread-popup ref="goodsSpreadPopupRef" />
@ -399,23 +181,14 @@
<goods-member-price-popup ref="memberPricePopupRef" @load="loadGoodsList" />
<!-- 批量设置弹出框 -->
<goods-batch-settings-popup
ref="goodsBatchSettingPopupRef"
@load="loadGoodsList"
/>
<goods-batch-settings-popup ref="goodsBatchSettingPopupRef" @load="loadGoodsList" />
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
debounce,
img,
filterDigit,
setTablePageStorage,
getTablePageStorage,
} from '@/utils/common'
import { debounce, img, filterDigit, setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { ElMessage, ElMessageBox, FormInstance } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { cloneDeep, multiply } from 'lodash-es'
@ -424,17 +197,7 @@ import goodsStockEditPopup from '@/addon/shop/views/goods/components/goods-stock
import goodsPriceEditPopup from '@/addon/shop/views/goods/components/goods-price-edit-popup.vue'
import goodsSpreadPopup from '@/addon/shop/views/goods/components/goods-spread-popup.vue'
import goodsBatchSettingsPopup from '@/addon/shop/views/goods/components/goods-batch-settings-popup.vue'
import {
getGoodsPageList,
getCategoryTree,
getGoodsType,
getBrandList,
getLabelList,
editGoodsSort,
editGoodsStatus,
copyGoods,
deleteGoods,
} from '@/addon/shop/api/goods'
import { getGoodsPageList, getCategoryTree, getGoodsType, getBrandList, getLabelList, editGoodsSort, editGoodsStatus, copyGoods, deleteGoods } from '@/addon/shop/api/goods'
import { getMemberLevelAll } from '@/app/api/member'
const router = useRouter()
@ -460,8 +223,8 @@ const goodsTable = reactive({
end_price: '',
status: route.query.status || '1',
order: '',
sort: '',
},
sort: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -469,7 +232,7 @@ const searchFormRef = ref<FormInstance>()
//
const regExp = {
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
digit: /^\d{0,10}(.?\d{0,2})$/
}
//
@ -484,14 +247,14 @@ const brandOptions: any = reactive([])
//
const labelOptions: any = reactive([])
const cascader = ref(null)
const cascader = ref(null);
// change
const handleCascaderChange = (value:any) => {
const cascaderInstance = cascader.value
const cascaderInstance = cascader.value;
if (cascaderInstance?.togglePopperVisible) {
cascaderInstance.togglePopperVisible(false) //
}
cascaderInstance.togglePopperVisible(false); //
}
};
//
const initData = () => {
@ -504,33 +267,29 @@ const initData = () => {
goodsCategoryTree.push({
value: '',
label: '全部',
children: [],
children: []
})
data.forEach((item: any) => {
const children: any = []
if (item.child_list) {
children.push({
value: item.category_id,
label: '全部',
label: '全部'
})
item.child_list.forEach((childItem: any) => {
children.push({
value: childItem.category_id,
label: childItem.category_name,
label: childItem.category_name
})
})
}
goodsCategoryTree.push({
value: item.category_id,
label: item.category_name,
children,
children
})
})
goodsCategoryOptions.splice(
0,
goodsCategoryOptions.length,
...goodsCategoryTree
)
goodsCategoryOptions.splice(0, goodsCategoryOptions.length, ...goodsCategoryTree)
}
})
@ -591,20 +350,13 @@ const handleSelectionChange = (val: []) => {
multipleSelection.value = val
toggleCheckbox.value = false
if (
multipleSelection.value.length > 0 &&
multipleSelection.value.length < goodsTable.data.length
) {
if (multipleSelection.value.length > 0 && multipleSelection.value.length < goodsTable.data.length) {
isIndeterminate.value = true
} else {
isIndeterminate.value = false
}
if (
multipleSelection.value.length == goodsTable.data.length &&
goodsTable.data.length &&
multipleSelection.value.length
) {
if (multipleSelection.value.length == goodsTable.data.length && goodsTable.data.length && multipleSelection.value.length) {
toggleCheckbox.value = true
}
}
@ -614,8 +366,8 @@ const previewEvent = (data: any) => {
const url = router.resolve({
path: '/preview/wap',
query: {
page: `/addon/shop/pages/goods/detail?goods_id=${data.goods_id}`,
},
page: `/addon/shop/pages/goods/detail?goods_id=${data.goods_id}`
}
})
window.open(url.href)
}
@ -640,19 +392,21 @@ const statusChange = (row: any, value: any) => {
if (value) {
editGoodsStatus({
goods_ids: row.goods_id,
status: value,
status: value
}).then((res) => {
loadGoodsList()
})
} else {
ElMessageBox.confirm(t('statusChangeTips'), t('warning'), {
ElMessageBox.confirm(t('statusChangeTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
type: 'warning'
}
).then(() => {
editGoodsStatus({
goods_ids: row.goods_id,
status: value,
status: value
}).then((res) => {
loadGoodsList()
})
@ -665,7 +419,7 @@ const batchGoodsStatus = (status: any) => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`,
message: `${t('batchEmptySelectedGoodsTips')}`
})
return
}
@ -677,7 +431,7 @@ const batchGoodsStatus = (status: any) => {
editGoodsStatus({
goods_ids: goodsIds,
status,
status
}).then((res) => {
loadGoodsList()
})
@ -688,7 +442,7 @@ const batchSetGoods = () => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`,
message: `${t('batchEmptySelectedGoodsTips')}`
})
return
}
@ -696,20 +450,23 @@ const batchSetGoods = () => {
}
/** ***************** 批量设置-end *************************/
const batchDeleteGoods = () => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`,
message: `${t('batchEmptySelectedGoodsTips')}`
})
return
}
ElMessageBox.confirm(t('batchGoodsDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('batchGoodsDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
type: 'warning'
}
).then(() => {
if (repeat.value) return
repeat.value = true
@ -719,13 +476,11 @@ const batchDeleteGoods = () => {
})
deleteGoods({
goods_ids: goodsIds,
})
.then(() => {
goods_ids: goodsIds
}).then(() => {
loadGoodsList()
repeat.value = false
})
.catch(() => {
}).catch(() => {
repeat.value = false
})
})
@ -736,7 +491,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !regExp.number.test(sort)) {
ElMessage({
type: 'warning',
message: `${t('sortTips')}`,
message: `${t('sortTips')}`
})
return
}
@ -745,7 +500,7 @@ const sortInputListener = debounce((sort, row) => {
}
editGoodsSort({
goods_id: row.goods_id,
sort,
sort
}).then((res) => {
// loadGoodsList();
})
@ -755,63 +510,45 @@ const sortInputListener = debounce((sort, row) => {
* 获取商品列表
*/
const loadGoodsList = (page: number = 1) => {
if (
goodsTable.searchParam.start_sale_num &&
!regExp.digit.test(goodsTable.searchParam.start_sale_num)
) {
if (goodsTable.searchParam.start_sale_num && !regExp.digit.test(goodsTable.searchParam.start_sale_num)) {
ElMessage({
type: 'warning',
message: `${t('startSaleNumTips')}`,
message: `${t('startSaleNumTips')}`
})
return
}
if (
goodsTable.searchParam.end_sale_num &&
!regExp.digit.test(goodsTable.searchParam.end_sale_num)
) {
if (goodsTable.searchParam.end_sale_num && !regExp.digit.test(goodsTable.searchParam.end_sale_num)) {
ElMessage({
type: 'warning',
message: `${t('endSaleNumTips')}`,
message: `${t('endSaleNumTips')}`
})
return
}
if (
Number(goodsTable.searchParam.start_sale_num) >
Number(goodsTable.searchParam.end_sale_num)
) {
if (Number(goodsTable.searchParam.start_sale_num) > Number(goodsTable.searchParam.end_sale_num)) {
ElMessage({
type: 'warning',
message: `${t('shopSaleNumTips')}`,
message: `${t('shopSaleNumTips')}`
})
return
}
if (
goodsTable.searchParam.start_price &&
!regExp.digit.test(goodsTable.searchParam.start_price)
) {
if (goodsTable.searchParam.start_price && !regExp.digit.test(goodsTable.searchParam.start_price)) {
ElMessage({
type: 'warning',
message: `${t('startPriceTips')}`,
message: `${t('startPriceTips')}`
})
return
}
if (
goodsTable.searchParam.end_price &&
!regExp.digit.test(goodsTable.searchParam.end_price)
) {
if (goodsTable.searchParam.end_price && !regExp.digit.test(goodsTable.searchParam.end_price)) {
ElMessage({
type: 'warning',
message: `${t('endPriceTips')}`,
message: `${t('endPriceTips')}`
})
return
}
if (
Number(goodsTable.searchParam.start_price) >
Number(goodsTable.searchParam.end_price)
) {
if (Number(goodsTable.searchParam.start_price) > Number(goodsTable.searchParam.end_price)) {
ElMessage({
type: 'warning',
message: `${t('shopPriceTips')}`,
message: `${t('shopPriceTips')}`
})
return
}
@ -823,16 +560,14 @@ const loadGoodsList = (page: number = 1) => {
getGoodsPageList({
page: goodsTable.page,
limit: goodsTable.limit,
...searchData,
})
.then((res) => {
...searchData
}).then(res => {
goodsTable.loading = false
goodsTable.data = res.data.data
goodsTable.total = res.data.total
multipleSelection.value = []
setTablePageStorage(goodsTable.page, goodsTable.limit, searchData)
})
.catch(() => {
}).catch(() => {
goodsTable.loading = false
})
}
@ -888,7 +623,7 @@ const spreadEvent = (data: any) => {
//
const memberLevel = ref([])
const getMemberLevelAllFn = () => {
getMemberLevelAll().then((res) => {
getMemberLevelAll().then(res => {
memberLevel.value = res.data ? res.data : []
})
}
@ -902,24 +637,24 @@ const memberPriceEvent = (data: any) => {
//
const copyEvent = (data: any) => {
ElMessageBox.confirm(t('goodsCopyTips'), t('warning'), {
ElMessageBox.confirm(t('goodsCopyTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
type: 'warning'
}
).then(() => {
if (repeat.value) return
repeat.value = true
copyGoods({
goods_id: data.goods_id,
})
.then((res: any) => {
goods_id: data.goods_id
}).then((res: any) => {
if (res.code == 1) {
loadGoodsList()
}
repeat.value = false
})
.catch((err) => {
}).catch(err => {
repeat.value = false
})
})
@ -927,21 +662,21 @@ const copyEvent = (data: any) => {
//
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('goodsDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('goodsDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
type: 'warning'
}
).then(() => {
if (repeat.value) return
repeat.value = true
deleteGoods({
goods_ids: id,
})
.then(() => {
goods_ids: id
}).then(() => {
loadGoodsList()
repeat.value = false
})
.catch(() => {
}).catch(() => {
repeat.value = false
})
})
@ -975,8 +710,7 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</style>
<style lang="scss" scoped>
.price-wrap,
.stock-wrap {
.price-wrap, .stock-wrap {
&:hover {
.icon-wrap {
visibility: visible;

579
admin/src/addon/shop/views/goods/public/js/useGoodsEdit.ts

File diff suppressed because it is too large

1147
admin/src/addon/shop/views/goods/real_edit.vue

File diff suppressed because it is too large

215
admin/src/addon/shop/views/goods/recycle.vue

@ -1,139 +1,67 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="goodsTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="goodsTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('goodsName')" prop="goods_name">
<el-input
v-model.trim="goodsTable.searchParam.goods_name"
:placeholder="t('goodsNamePlaceholder')"
maxlength="60"
/>
<el-input v-model.trim="goodsTable.searchParam.goods_name" :placeholder="t('goodsNamePlaceholder')" maxlength="60" />
</el-form-item>
<el-form-item :label="t('goodsCategory')" prop="goods_category">
<el-cascader
v-model="goodsTable.searchParam.goods_category"
:options="goodsCategoryOptions"
:placeholder="t('goodsCategoryPlaceholder')"
clearable
:props="{ value: 'value', label: 'label', emitPath: false }"
/>
<el-cascader v-model="goodsTable.searchParam.goods_category" :options="goodsCategoryOptions" :placeholder="t('goodsCategoryPlaceholder')" clearable :props="{ value: 'value', label: 'label', emitPath:false }" />
</el-form-item>
<el-form-item :label="t('goodsType')" prop="goods_type">
<el-select
v-model="goodsTable.searchParam.goods_type"
:placeholder="t('goodsTypePlaceholder')"
clearable
>
<el-option
v-for="item in goodsType"
:key="item.type"
:label="item.name"
:value="item.type"
/>
<el-select v-model="goodsTable.searchParam.goods_type" :placeholder="t('goodsTypePlaceholder')" clearable>
<el-option v-for="item in goodsType" :key="item.type" :label="item.name" :value="item.type" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadGoodsList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadGoodsList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<div class="mb-[10px] flex items-center">
<el-checkbox
v-model="toggleCheckbox"
size="large"
class="px-[14px]"
@change="toggleChange"
:indeterminate="isIndeterminate"
/>
<el-button @click="batchRecycle" size="small">{{
t('batchRecycle')
}}</el-button>
<el-checkbox v-model="toggleCheckbox" size="large" class="px-[14px]" @change="toggleChange" :indeterminate="isIndeterminate" />
<el-button @click="batchRecycle" size="small">{{ t('batchRecycle') }}</el-button>
</div>
<el-table
:data="goodsTable.data"
size="large"
v-loading="goodsTable.loading"
ref="goodsListTableRef"
@sort-change="sortChange"
@selection-change="handleSelectionChange"
>
<el-table :data="goodsTable.data" size="large" v-loading="goodsTable.loading" ref="goodsListTableRef" @sort-change="sortChange" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ !goodsTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
prop="goods_id"
:label="t('goodsInfo')"
min-width="300"
>
<el-table-column prop="goods_id" :label="t('goodsInfo')" min-width="300">
<template #default="{ row }">
<div class="flex items-center cursor-pointer">
<div
class="min-w-[70px] h-[70px] flex items-center justify-center"
>
<img
class="max-w-[70px] max-h-[70px]"
:src="img(row.goods_cover_thumb_small)"
/>
<div class="min-w-[70px] h-[70px] flex items-center justify-center">
<img class="max-w-[70px] max-h-[70px]" :src="img(row.goods_cover_thumb_small)" />
</div>
<div class="ml-2">
<span :title="row.goods_name" class="multi-hidden">{{
row.goods_name
}}</span>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
<span :title="row.goods_name" class="multi-hidden">{{ row.goods_name }}</span>
<span class="text-primary text-[12px]">{{ row.goods_type_name }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column
prop="price"
:label="t('price')"
min-width="120"
align="right"
sortable="custom"
>
<el-table-column prop="price" :label="t('price')" min-width="120" align="right" sortable="custom">
<template #default="{ row }">
<div>{{ row.goodsSku.price }}</div>
</template>
</el-table-column>
<el-table-column
prop="stock"
:label="t('stock')"
min-width="120"
sortable="custom"
/>
<el-table-column
prop="sale_num"
:label="t('saleNum')"
min-width="100"
sortable="custom"
/>
<el-table-column prop="stock" :label="t('stock')" min-width="120" sortable="custom" />
<el-table-column prop="sale_num" :label="t('saleNum')" min-width="100" sortable="custom" />
<el-table-column prop="status" :label="t('status')" min-width="100">
<template #default="{ row }">
<div v-if="row.status == 1">{{ t('statusOn') }}</div>
@ -141,29 +69,18 @@
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="150"
sortable="custom"
>
<el-table-column prop="create_time" :label="t('createTime')" min-width="150" sortable="custom">
<template #default="{ row }">
<div>{{ row.create_time }}</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="recycleEvent(row)">{{
t('recycle')
}}</el-button>
<el-button type="primary" link @click="recycleEvent(row)">{{ t('recycle') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<!-- <div class="flex items-center flex-1">
@ -171,16 +88,12 @@
<el-button @click="batchRecycle" size="small">{{ t('batchRecycle') }}</el-button>
</div> -->
<el-pagination
v-model:current-page="goodsTable.page"
v-model:page-size="goodsTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="goodsTable.total"
@size-change="loadGoodsList()"
@current-change="loadGoodsList"
/>
<el-pagination v-model:current-page="goodsTable.page" v-model:page-size="goodsTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="goodsTable.total"
@size-change="loadGoodsList()" @current-change="loadGoodsList" />
</div>
</div>
</el-card>
</div>
</template>
@ -197,7 +110,7 @@ import {
getRecycleGoodsPageList,
getCategoryTree,
getGoodsType,
recycleGoods,
recycleGoods
} from '@/addon/shop/api/goods'
const route = useRoute()
@ -215,8 +128,8 @@ const goodsTable = reactive({
goods_category: [],
goods_type: '',
order: '',
sort: '',
},
sort: ''
}
})
const searchFormRef = ref()
@ -240,21 +153,17 @@ const initData = () => {
item.child_list.forEach((childItem: any) => {
children.push({
value: childItem.category_id,
label: childItem.category_name,
label: childItem.category_name
})
})
}
goodsCategoryTree.push({
value: item.category_id,
label: item.category_name,
children,
children
})
})
goodsCategoryOptions.splice(
0,
goodsCategoryOptions.length,
...goodsCategoryTree
)
goodsCategoryOptions.splice(0, goodsCategoryOptions.length, ...goodsCategoryTree)
}
})
//
@ -292,20 +201,13 @@ const handleSelectionChange = (val: []) => {
multipleSelection.value = val
toggleCheckbox.value = false
if (
multipleSelection.value.length > 0 &&
multipleSelection.value.length < goodsTable.data.length
) {
if (multipleSelection.value.length > 0 && multipleSelection.value.length < goodsTable.data.length) {
isIndeterminate.value = true
} else {
isIndeterminate.value = false
}
if (
multipleSelection.value.length == goodsTable.data.length &&
goodsTable.data.length &&
multipleSelection.value.length
) {
if (multipleSelection.value.length == goodsTable.data.length && goodsTable.data.length && multipleSelection.value.length) {
toggleCheckbox.value = true
}
}
@ -315,16 +217,18 @@ const batchRecycle = () => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`,
message: `${t('batchEmptySelectedGoodsTips')}`
})
return
}
ElMessageBox.confirm(t('batchGoodsRecycleTips'), t('warning'), {
ElMessageBox.confirm(t('batchGoodsRecycleTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
type: 'warning'
}
).then(() => {
if (repeat.value) return
repeat.value = true
@ -334,14 +238,12 @@ const batchRecycle = () => {
})
recycleGoods({
goods_ids: goodsIds,
})
.then(() => {
goods_ids: goodsIds
}).then(() => {
loadGoodsList()
toggleCheckbox.value = false
repeat.value = false
})
.catch(() => {
}).catch(() => {
repeat.value = false
})
})
@ -359,14 +261,12 @@ const loadGoodsList = (page: number = 1) => {
getRecycleGoodsPageList({
page: goodsTable.page,
limit: goodsTable.limit,
...searchData,
})
.then((res) => {
...searchData
}).then(res => {
goodsTable.loading = false
goodsTable.data = res.data.data
goodsTable.total = res.data.total
})
.catch(() => {
}).catch(() => {
goodsTable.loading = false
})
}
@ -375,24 +275,24 @@ loadGoodsList()
//
const recycleEvent = (data: any) => {
ElMessageBox.confirm(t('goodsRecycleTips'), t('warning'), {
ElMessageBox.confirm(t('goodsRecycleTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
type: 'warning'
}
).then(() => {
if (repeat.value) return
repeat.value = true
recycleGoods({
goods_ids: data.goods_id,
})
.then((res: any) => {
goods_ids: data.goods_id
}).then((res: any) => {
if (res.code == 1) {
loadGoodsList()
}
repeat.value = false
})
.catch(() => {
}).catch(() => {
repeat.value = false
})
})
@ -419,6 +319,7 @@ const sortChange = (event: any) => {
}
loadGoodsList()
}
</script>
<style lang="scss" scoped>

95
admin/src/addon/shop/views/goods/service.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{pageName}}</span>
<el-button type="primary" @click="addEvent">
@ -8,47 +9,25 @@
</el-button>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="serveTable.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="serveTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('serviceName')" prop="service_name">
<el-input
v-model.trim="serveTable.searchParam.service_name"
:placeholder="t('serviceNamePlaceholder')"
/>
<el-input v-model.trim="serveTable.searchParam.service_name" :placeholder="t('serviceNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadServeList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadServeList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="serveTable.data"
size="large"
v-loading="serveTable.loading"
>
<el-table :data="serveTable.data" size="large" v-loading="serveTable.loading">
<template #empty>
<span>{{ !serveTable.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="service_name"
:label="t('serviceName')"
min-width="120"
/>
<el-table-column prop="service_name" :label="t('serviceName')" min-width="120" />
<!-- <el-table-column :label="t('image')" min-width="120">-->
<!-- <template #default="{ row }">-->
<!-- <div class="h-[50px]">-->
@ -63,34 +42,18 @@
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column prop="desc" :label="t('desc')" min-width="120" />
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.service_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row.service_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="serveTable.page"
v-model:page-size="serveTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="serveTable.total"
@size-change="loadServeList()"
@current-change="loadServeList"
/>
<el-pagination v-model:current-page="serveTable.page" v-model:page-size="serveTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="serveTable.total"
@size-change="loadServeList()" @current-change="loadServeList" />
</div>
</div>
@ -117,8 +80,8 @@ const serveTable = reactive({
loading: true,
data: [],
searchParam: {
service_name: '',
},
service_name: ''
}
})
const searchFormRef = ref<FormInstance>()
@ -133,14 +96,12 @@ const loadServeList = (page: number = 1) => {
getServePageList({
page: serveTable.page,
limit: serveTable.limit,
...serveTable.searchParam,
})
.then((res) => {
...serveTable.searchParam
}).then(res => {
serveTable.loading = false
serveTable.data = res.data.data
serveTable.total = res.data.total
})
.catch(() => {
}).catch(() => {
serveTable.loading = false
})
}
@ -169,16 +130,17 @@ const editEvent = (data: any) => {
* 删除商品服务
*/
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('serveDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('serveDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteServe(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteServe(id).then(() => {
loadServeList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -189,4 +151,5 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

1066
admin/src/addon/shop/views/goods/virtual_edit.vue

File diff suppressed because it is too large

181
admin/src/addon/shop/views/index/index.vue

@ -3,9 +3,7 @@
<!-- 实时概况 -->
<el-card shadow="never" class="!border-none">
<template #header>
<span class="text-lg font-extrabold mr-[10px]">{{
t('realtimeOverview')
}}</span>
<span class="text-lg font-extrabold mr-[10px]">{{t('realtimeOverview')}}</span>
<span class="text-sm text-[#a19f98]">{{t('updateTime')}}</span>
<span class="text-sm text-[#a19f98]">{{ time }}</span>
</template>
@ -17,12 +15,7 @@
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">{{t('todayOrderCount')}}</span>
<el-tooltip
class="box-item"
effect="light"
:content="t('todayOrderCount')"
placement="top"
>
<el-tooltip class="box-item" effect="light" :content="t('todayOrderCount')" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
@ -36,10 +29,7 @@
<span>{{statYesterday.order_num}}</span>
</div>
<div class="text-sm text-[#a19f98] leading-8 mt-[15px]">
<el-statistic
:title="t('orderCount')"
:value="statTotal.order_num"
/>
<el-statistic :title="t('orderCount')" :value="statTotal.order_num" />
</div>
</div>
</el-col>
@ -50,12 +40,7 @@
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">{{t('todayOrderSale')}}</span>
<el-tooltip
class="box-item"
effect="light"
:content="t('todayOrderSale')"
placement="top"
>
<el-tooltip class="box-item" effect="light" :content="t('todayOrderSale')" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
@ -69,10 +54,7 @@
<span>{{statYesterday.sale_money}}</span>
</div>
<div class="text-sm text-[#a19f98] leading-8 mt-[15px]">
<el-statistic
:title="t('salesTotal')"
:value="statTotal.sale_money"
/>
<el-statistic :title="t('salesTotal')" :value="statTotal.sale_money" />
</div>
</div>
</el-col>
@ -83,12 +65,7 @@
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">{{t('todayAddMemberCount')}}</span>
<el-tooltip
class="box-item"
effect="light"
:content="t('todayAddMemberCount')"
placement="top"
>
<el-tooltip class="box-item" effect="light" :content="t('todayAddMemberCount')" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
@ -102,10 +79,7 @@
<span>{{statYesterday.refund_money}}</span>
</div>
<div class="text-sm text-[#a19f98] leading-8 mt-[15px]">
<el-statistic
:title="t('memberTotal')"
:value="statTotal.refund_money"
/>
<el-statistic :title="t('memberTotal')" :value="statTotal.refund_money" />
</div>
</div>
</el-col>
@ -116,12 +90,7 @@
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">{{t('todayBrowseCount')}}</span>
<el-tooltip
class="box-item"
effect="light"
:content="t('todayBrowseCount')"
placement="top"
>
<el-tooltip class="box-item" effect="light" :content="t('todayBrowseCount')" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
@ -135,10 +104,7 @@
<span>{{statYesterday.access_sum}}</span>
</div>
<div class="text-sm text-[#a19f98] leading-8 mt-[15px]">
<el-statistic
:title="t('browseTotal')"
:value="statTotal.access_sum"
/>
<el-statistic :title="t('browseTotal')" :value="statTotal.access_sum" />
</div>
</div>
</el-col>
@ -152,24 +118,13 @@
<span class="text-lg font-extrabold">{{t('agentMatters')}}</span>
</template>
<el-row>
<el-col
:span="4"
class="cursor-pointer"
@click="
router.push({ path: '/shop/order/index', query: { status: 1 } })
"
>
<el-col :span="4" class="cursor-pointer" @click="router.push({ path: '/shop/order/index', query: {status: 1}})">
<div class="ml-[10px]">
<el-statistic :value="statOrder.wait_pay_order">
<template #title>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">{{t('waitPayOrder')}}</span>
<el-tooltip
class="box-item"
effect="light"
:content="t('waitPayOrder')"
placement="top"
>
<el-tooltip class="box-item" effect="light" :content="t('waitPayOrder')" placement="top">
<el-icon>
<QuestionFilled />
</el-icon>
@ -179,74 +134,38 @@
</el-statistic>
</div>
</el-col>
<el-col
:span="4"
class="cursor-pointer"
@click="
router.push({ path: '/shop/order/index', query: { status: 2 } })
"
>
<el-col :span="4" class="cursor-pointer" @click="router.push({ path: '/shop/order/index', query: {status: 2}})">
<el-statistic :value="statOrder.wait_delivery_order">
<template #title>
<div style="display: inline-flex; align-items: center">
{{ t('waitDeliveryOrder') }}
</div>
<div style="display: inline-flex; align-items: center">{{t('waitDeliveryOrder')}}</div>
</template>
</el-statistic>
</el-col>
<el-col
:span="4"
class="cursor-pointer"
@click="
router.push({ path: '/shop/order/index', query: { status: 3 } })
"
>
<el-col :span="4" class="cursor-pointer" @click="router.push({ path: '/shop/order/index', query: {status: 3}})">
<el-statistic :value="statOrder.wait_take_order">
<template #title>
<div style="display: inline-flex; align-items: center">
{{ t('waitTakeOrder') }}
</div>
<div style="display: inline-flex; align-items: center">{{t('waitTakeOrder')}}</div>
</template>
</el-statistic>
</el-col>
<el-col
:span="4"
class="cursor-pointer"
@click="router.push({ path: '/shop/order/refund' })"
>
<el-col :span="4" class="cursor-pointer" @click="router.push({ path: '/shop/order/refund'})">
<el-statistic :value="statOrder.refund_order">
<template #title>
<div style="display: inline-flex; align-items: center">
退款订单
</div>
<div style="display: inline-flex; align-items: center">退款订单</div>
</template>
</el-statistic>
</el-col>
<el-col
:span="4"
class="cursor-pointer"
@click="router.push({ path: '/shop/goods/list' })"
>
<el-col :span="4" class="cursor-pointer" @click="router.push({ path: '/shop/goods/list'})">
<el-statistic :value="statGoods.sale_goods_num">
<template #title>
<div style="display: inline-flex; align-items: center">
{{ t('saleGoodsNum') }}
</div>
<div style="display: inline-flex; align-items: center">{{t('saleGoodsNum')}}</div>
</template>
</el-statistic>
</el-col>
<el-col
:span="4"
class="cursor-pointer"
@click="
router.push({ path: '/shop/goods/list', query: { status: 0 } })
"
>
<el-col :span="4" class="cursor-pointer" @click="router.push({ path: '/shop/goods/list', query: {status: 0}})">
<el-statistic :value="statGoods.warehouse_goods_num">
<template #title>
<div style="display: inline-flex; align-items: center">
{{ t('warehouseGoodsNum') }}
</div>
<div style="display: inline-flex; align-items: center">{{t('warehouseGoodsNum')}}</div>
</template>
</el-statistic>
</el-col>
@ -261,10 +180,7 @@
<template #header>
<span class="text-lg font-extrabold">订单量趋势</span>
</template>
<div
ref="visitStat"
:style="{ width: '100%', height: '300px' }"
></div>
<div ref="visitStat" :style="{ width: '100%', height: '300px' }"></div>
</el-card>
</el-col>
<el-col :span="12">
@ -289,7 +205,7 @@ import {
getShopYesterdayCountList,
getShopStat,
getShopOrderStat,
getShopGoodsStat,
getShopGoodsStat
} from '@/addon/shop/api/shop'
import * as echarts from 'echarts'
import { useRouter } from 'vue-router'
@ -299,36 +215,36 @@ const visitStat = ref<any>(null)
const hourStat = ref<any>(null)
interface statTotalType{
order_num: number
sale_money: number
refund_money: number
order_num: number,
sale_money: number,
refund_money: number,
access_sum: number
}
interface statTodayType{
order_num: number
sale_money: number
refund_money: number
order_num: number,
sale_money: number,
refund_money: number,
access_sum: number
}
interface statYesterdayType{
order_num: number
sale_money: number
refund_money: number
order_num: number,
sale_money: number,
refund_money: number,
access_sum: number
}
interface statOrderType{
wait_pay_order: number
wait_delivery_order: number
wait_take_order: number
wait_pay_order: number,
wait_delivery_order: number,
wait_take_order: number,
refund_order: number
}
interface statGoodsType{
sale_goods_num: number
sale_goods_num: number,
warehouse_goods_num: number
}
interface statCountType{
order_num: number
time: string
order_num: number,
time: string,
sale_money: number
}
const statTotal = ref<statTotalType|any>([])
@ -342,6 +258,7 @@ const getStatInfoFn = async () => {
let statTotalData = await (await getShopCountList()).data
for( let i in statTotalData){
statTotalData[i] = Number(statTotalData[i])
}
statTotal.value = statTotalData
statToday.value = await (await getShopTodayCountList()).data
@ -367,18 +284,18 @@ const drawChart = (item: any) => {
// },
legend: {},
xAxis: {
data: [],
data: []
},
yAxis: {},
tooltip: {
trigger: 'axis',
trigger: 'axis'
},
series: [
{
type: 'line',
data: [],
},
],
data: []
}
]
})
visitStatOption.value.xAxis.data = statCount.value.time
visitStatOption.value.series[0].data = value
@ -395,18 +312,18 @@ const drawChartTo = (item: any) => {
// },
legend: {},
xAxis: {
data: [],
data: []
},
yAxis: {},
tooltip: {
trigger: 'axis',
trigger: 'axis'
},
series: [
{
type: 'line',
data: [],
},
],
data: []
}
]
})
hourStatOption.value.xAxis.data = statCount.value.time
hourStatOption.value.series[0].data = valueTo

247
admin/src/addon/shop/views/marketing/coupon/add.vue

@ -1,40 +1,22 @@
<template>
<div class="main-container">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<!-- 表单 -->
<el-card class="box-card mt-[15px] !border-none" shadow="never">
<el-form
class="page-form"
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
>
<el-form class="page-form" :model="formData" label-width="120px" ref="formRef" :rules="formRules">
<!-- 优惠券信息 -->
<!-- 优惠券名称 -->
<el-form-item :label="t('title')" prop="title">
<el-input
v-model.trim="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
:maxlength="20"
/>
<el-input v-model.trim="formData.title" clearable :placeholder="t('titlePlaceholder')" class="input-width" :maxlength="20" />
</el-form-item>
<!-- 优惠券面额 -->
<el-form-item :label="t('price')" prop="price">
<el-input
v-model.trim="formData.price"
clearable
:placeholder="t('pricePlaceholder')"
class="input-width"
maxlength="5"
@keyup="filterDigit($event)"
>
<el-input v-model.trim="formData.price" clearable :placeholder="t('pricePlaceholder')" class="input-width" maxlength="5" @keyup="filterDigit($event)">
<template #append></template>
</el-input>
</el-form-item>
@ -50,27 +32,14 @@
<el-form-item v-if="formData.type == 2" prop="goods_category_ids">
<div>
<el-cascader
v-model="formData.goods_category_ids"
:options="options"
:props="props"
placeholder="请选择商品分类"
collapse-tags
collapse-tags-tooltip
clearable
/>
<el-cascader v-model="formData.goods_category_ids" :options="options" :props="props" placeholder="请选择商品分类" collapse-tags collapse-tags-tooltip clearable />
</div>
</el-form-item>
<el-form-item v-if="formData.type == 3" prop="goods_ids">
<div>
<el-form-item>
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="formData.goods_ids"
:min="1"
:max="99"
/>
<goods-select-popup ref="goodsSelectPopupRef" v-model="formData.goods_ids" :min="1" :max="99" />
</el-form-item>
</div>
</el-form-item>
@ -86,12 +55,7 @@
<el-form-item v-if="formData.threshold == 1" prop="min_condition_money">
最低满
<div class="flex items-center px-[5px]">
<el-input
v-model.trim="formData.min_condition_money"
@keyup="filterDigit($event)"
clearable
class="!w-[100px]"
/>
<el-input v-model.trim="formData.min_condition_money" @keyup="filterDigit($event)" clearable class="!w-[100px]" />
</div>
元可用
</el-form-item>
@ -107,12 +71,7 @@
<el-form-item v-show="formData.valid_type == 1">
领劵后立即生效有效期
<div class="flex items-center px-[5px]">
<el-input
v-model.trim="formData.length"
@keyup="filterNumber($event)"
clearable
class="!w-[100px]"
/>
<el-input v-model.trim="formData.length" @keyup="filterNumber($event)" clearable class="!w-[100px]" />
</div>
</el-form-item>
@ -133,64 +92,36 @@
<el-radio :label="2">{{ t('grant') }}</el-radio>
</el-radio-group>
</div>
<div class="form-tip">
开启手动领取后会员可以直接在优惠券列表以及优惠券推广中直接领取
</div>
<div class="form-tip">开启手动领取后会员可以直接在优惠券列表以及优惠券推广中直接领取</div>
</el-form-item>
<!-- 领取时间 -->
<el-form-item
:label="t('receiveTime')"
v-show="formData.receive_type == 1"
>
<el-form-item :label="t('receiveTime')" v-show="formData.receive_type == 1">
<el-radio-group v-model="formData.receive_type_time">
<el-radio :label="1">{{ t('limitedTime') }}</el-radio>
<el-radio :label="2">{{ t('unlimitedTime') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
prop="receive_time"
v-show="formData.receive_type_time == 1 && formData.receive_type == 1"
>
<el-form-item prop="receive_time" v-show="formData.receive_type_time == 1 && formData.receive_type == 1">
<div class="w-[180px]">
<el-date-picker
v-model="formData.receive_time"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
<el-date-picker v-model="formData.receive_time" type="datetimerange" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期"/>
</div>
</el-form-item>
<!-- 优惠券数量 -->
<el-form-item
:label="t('receiveNumber')"
v-show="formData.receive_type == 1"
>
<el-form-item :label="t('receiveNumber')" v-show="formData.receive_type == 1">
<el-radio-group v-model="formData.limit">
<el-radio :label="1">{{ t('limit') }}</el-radio>
<el-radio :label="2">{{ t('unlimited') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-show="formData.limit == 1 && formData.receive_type == 1"
prop="remain_count"
>
<el-form-item v-show="formData.limit == 1 && formData.receive_type == 1" prop="remain_count">
<div>
<el-input
onkeypress="return( /[\d]/.test(String.fromCharCode(event.keyCode) ) )"
v-model.trim="formData.remain_count"
clearable
:placeholder="t('remainCountPlaceholder')"
class="input-width"
:min="1"
:max="100000"
:controls="false"
maxlength="6"
>
<el-input onkeypress='return( /[\d]/.test(String.fromCharCode(event.keyCode) ) )'
v-model.trim="formData.remain_count" clearable :placeholder="t('remainCountPlaceholder')"
class="input-width" :min="1" :max="100000" :controls="false" maxlength="6">
<template #append></template>
</el-input>
</div>
@ -198,33 +129,21 @@
</el-form-item>
<!-- 用户可领取数量 -->
<el-form-item
:label="t('userLimitCount')"
prop="limit_count"
v-show="formData.receive_type == 1"
>
<el-input
onkeypress="return( /[\d]/.test(String.fromCharCode(event.keyCode) ) )"
v-model.trim="formData.limit_count"
clearable
:placeholder="t('userLimitCountPlaceholder')"
class="input-width"
:min="1"
:max="100000"
maxlength="6"
>
<el-form-item :label="t('userLimitCount')" prop="limit_count" v-show="formData.receive_type == 1">
<el-input onkeypress='return( /[\d]/.test(String.fromCharCode(event.keyCode) ) )'
v-model.trim="formData.limit_count" clearable :placeholder="t('userLimitCountPlaceholder')"
class="input-width" :min="1" :max="100000" maxlength="6">
<template #append></template>
</el-input>
</el-form-item>
</el-form>
</el-card>
<!-- 提交按钮 -->
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
@ -289,7 +208,7 @@ const formData = ref<FormDataType>({
receive_type_time: 2,
valid_type: 1, // 1=2=
receive_time: [start, end],
valid_time: end,
valid_time: end
})
const formRef = ref<FormInstance>()
@ -298,41 +217,41 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' }
],
price: [
{ required: true, validator: priceRule, trigger: 'blur' }
],
goods_ids: [
{ required: true, message: t('请选择商品'), trigger: 'blur' }
],
price: [{ required: true, validator: priceRule, trigger: 'blur' }],
goods_ids: [{ required: true, message: t('请选择商品'), trigger: 'blur' }],
goods_category_ids: [
{ required: true, message: t('请选择商品分类'), trigger: 'blur' },
{ required: true, message: t('请选择商品分类'), trigger: 'blur' }
],
min_condition_money: [
{ required: true, validator: minConditionMoney, trigger: 'blur' },
{ required: true, validator: minConditionMoney, trigger: 'blur' }
],
valid_time: [{ required: true, validator: validTime, trigger: 'blur' }],
receive_time: [{ required: true, validator: receiveTime, trigger: 'blur' }],
remain_count: [{ required: true, validator: remainCount, trigger: 'blur' }],
limit_count: [
{ required: true, validator: limitCountRule, trigger: 'blur' },
valid_time:[
{ required: true, validator: validTime, trigger: 'blur' }
],
receive_time:[
{ required: true, validator: receiveTime, trigger: 'blur' }
],
remain_count: [
{ required: true, validator: remainCount, trigger: 'blur' }
],
limit_count: [
{ required: true, validator: limitCountRule, trigger: 'blur' }
]
}
})
const receiveTime = (rule: any, value: any, callback: any) => {
if (
formData.value.receive_type_time == 1 &&
formData.value.receive_type == 1
) {
if (
!formData.value.receive_time[0] ||
timestampFn(formData.value.receive_time[0]) <= Date.now()
) {
if(formData.value.receive_type_time == 1 && formData.value.receive_type == 1){
if (!formData.value.receive_time[0] || timestampFn(formData.value.receive_time[0]) <= Date.now()) {
callback(new Error(t('领取开始时间不能小于等于当前时间')))
}
if (
!formData.value.receive_time[1] ||
timestampFn(formData.value.receive_time[1]) <=
timestampFn(formData.value.receive_time[0])
) {
if (!formData.value.receive_time[1] || timestampFn(formData.value.receive_time[1]) <= timestampFn(formData.value.receive_time[0])) {
callback(new Error(t('领取结束时间不能小于等于领取开始时间')))
}
}
@ -340,21 +259,11 @@ const receiveTime = (rule: any, value: any, callback: any) => {
}
const validTime = (rule: any, value: any, callback: any) => {
if (
formData.value.valid_type == 2 &&
formData.value.valid_time <= Date.now()
) {
if (formData.value.valid_type == 2 && formData.value.valid_time <= Date.now()) {
callback(new Error(t('有效期不能小于等于当前时间')))
}
if (
formData.value.valid_type == 2 &&
formData.value.receive_type_time == 1 &&
formData.value.receive_type == 1
) {
if (
timestampFn(formData.value.valid_time) <=
timestampFn(formData.value.receive_time[1])
) {
if(formData.value.valid_type == 2 && formData.value.receive_type_time == 1 && formData.value.receive_type == 1){
if (timestampFn(formData.value.valid_time) <= timestampFn(formData.value.receive_time[1])) {
callback(new Error(t('有效期不能小于等于领取结束时间')))
}
}
@ -362,54 +271,33 @@ const validTime = (rule: any, value: any, callback: any) => {
}
const minConditionMoney = (rule: any, value: any, callback: any) => {
if (
formData.value.threshold == 1 &&
formData.value.min_condition_money <= 0
) {
if (formData.value.threshold == 1 && formData.value.min_condition_money <= 0 ) {
callback(new Error(t('使用门槛最低不能小于0元')))
}
callback()
}
const remainCount = (rule: any, value: any, callback: any) => {
if (
formData.value.remain_count != '' &&
formData.value.remain_count > 100000
) {
if (formData.value.remain_count != '' && formData.value.remain_count > 100000) {
callback(new Error(t('remainCountPlaceholder')))
}
if (
!formData.value.remain_count ||
(formData.value.remain_count != '' && formData.value.remain_count < 1)
) {
if (!formData.value.remain_count || formData.value.remain_count != '' && formData.value.remain_count < 1) {
callback(new Error(t('发放数量不能小于1张')))
}
callback()
}
const priceRule = (rule: any, value: any, callback: any) => {
if (
!formData.value.price ||
formData.value.price == '' ||
formData.value.price <= 0
) {
if (!formData.value.price || formData.value.price == '' || formData.value.price <= 0) {
callback(new Error(t('pricePlaceholder')))
}
callback()
}
const limitCountRule = (rule: any, value: any, callback: any) => {
if (
!formData.value.limit_count ||
(formData.value.limit_count != '' && formData.value.limit_count < 1)
) {
if (!formData.value.limit_count || formData.value.limit_count != '' && formData.value.limit_count < 1) {
callback(new Error(t('userLimitCountPlaceholder')))
}
if (
formData.value.limit == 1 &&
formData.value.limit_count != '' &&
formData.value.remain_count != '' &&
parseInt(formData.value.limit_count) > parseInt(formData.value.remain_count)
) {
if (formData.value.limit == 1 && formData.value.limit_count != '' && formData.value.remain_count != '' && parseInt(formData.value.limit_count) > parseInt(formData.value.remain_count)) {
callback(new Error(t('限领张数不能大于发放数量')))
}
callback()
@ -417,8 +305,8 @@ const limitCountRule = (rule: any, value: any, callback: any) => {
//
const timestampFn = (data)=>{
var dateObject = new Date(data)
return dateObject.getTime()
var dateObject = new Date(data);
return dateObject.getTime();
}
//
@ -435,22 +323,20 @@ const onSave = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
let data = cloneDeep(formData.value)
let data = cloneDeep(formData.value);
if(data.type == 1){
delete data.goods_category_ids
delete data.goods_ids
delete data.goods_category_ids;
delete data.goods_ids;
}else if(data.type == 2){
delete data.goods_ids
delete data.goods_ids;
}else if(data.type == 3){
delete data.goods_category_ids
delete data.goods_category_ids;
}
const save = addCoupon
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
history.back()
})
.catch((err) => {
}).catch(err => {
loading.value = false
})
}
@ -460,6 +346,7 @@ const onSave = async (formEl: FormInstance | undefined) => {
const back = () => {
router.push('/shop/marketing/coupon/list')
}
</script>
<style lang="scss">
@ -473,7 +360,7 @@ input::-webkit-inner-spin-button {
margin: 0;
}
input[type='number'] {
input[type="number"] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
-o-appearance: textfield;

133
admin/src/addon/shop/views/marketing/coupon/components/coupon-collection.vue

@ -1,23 +1,11 @@
<template>
<el-drawer
v-model="showDialog"
:title="popTitle"
direction="rtl"
:before-close="handleClose"
class="collection-detail-drawer"
>
<el-drawer v-model="showDialog" :title="popTitle" direction="rtl" :before-close="handleClose" class="collection-detail-drawer">
<div class="main-container" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<el-row class="flex">
<el-col :span="8" class="min-w-[100px]">
<div class="statistic-card">
<el-statistic
:value="
couponStatistics.receive_count
? Number.parseInt(couponStatistics.receive_count)
: '0'
"
></el-statistic>
<el-statistic :value="couponStatistics.receive_count ? Number.parseInt(couponStatistics.receive_count) : '0'"></el-statistic>
<div class="statistic-footer">
<div class="footer-item text-[14px] text-[#666]">
<span>{{ t('receiveCount') }}</span>
@ -27,13 +15,7 @@
</el-col>
<el-col :span="8" class="min-w-[100px]">
<div class="statistic-card">
<el-statistic
:value="
couponStatistics.receive_use_count
? Number.parseInt(couponStatistics.receive_use_count)
: '0'
"
></el-statistic>
<el-statistic :value="couponStatistics.receive_use_count ? Number.parseInt(couponStatistics.receive_use_count) : '0'"></el-statistic>
<div class="statistic-footer">
<div class="footer-item text-[14px] text-[#666]">
<span>{{ t('receiveUseCount') }}</span>
@ -43,13 +25,7 @@
</el-col>
<el-col :span="8" class="min-w-[100px]">
<div class="statistic-card">
<el-statistic
:value="
couponStatistics.receive_expire_count
? Number.parseInt(couponStatistics.receive_expire_count)
: '0'
"
></el-statistic>
<el-statistic :value="couponStatistics.receive_expire_count ? Number.parseInt(couponStatistics.receive_expire_count) : '0'"></el-statistic>
<div class="statistic-footer">
<div class="footer-item text-[14px] text-[#666]">
<span>{{ t('receiveExpireCount') }}</span>
@ -60,40 +36,20 @@
</el-row>
</el-card>
<el-card
class="box-card !border-none mb-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="tableData.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none mb-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('memberInfo')" prop="keywords">
<el-input
v-model.trim="tableData.searchParam.keywords"
class="w-[240px]"
:placeholder="t('memberInfoPlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.keywords" class="w-[240px]" :placeholder="t('memberInfoPlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="couponCollection()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="couponCollection()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<div class="mt-[10px]">
<el-table
:data="tableData.data"
ref="tableRef"
size="large"
v-loading="tableData.loading"
>
<el-table :data="tableData.data" ref="tableRef" size="large" v-loading="tableData.loading">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
@ -102,19 +58,12 @@
<el-table-column :label="t('userName')">
<template #default="{ row }">
<div class="flex flex-col">
<span class="text-[12px] mt-[5px]">{{
row.member.nickname
}}</span>
<span class="text-[12px] mt-[5px]">
{{ row.member.mobile }}</span
>
<span class="text-[12px] mt-[5px]">{{ row.member.nickname }}</span>
<span class="text-[12px] mt-[5px]"> {{ row.member.mobile }}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="receive_type_name"
:label="t('collectionReceiveType')"
/>
<el-table-column prop="receive_type_name" :label="t('collectionReceiveType')" />
<el-table-column prop="create_time" :label="t('createTime')" />
<el-table-column prop="expire_time" :label="t('expireTime')" />
<el-table-column prop="status_name" :label="t('status')" />
@ -130,29 +79,19 @@
</el-table-column>
<el-table-column :label="t('operation')" fixed="right" align="right">
<template #default="{ row }">
<el-button
type="primary"
v-if="row.use_time != 0"
link
@click="showOrder(row)"
>{{ t('showOrder') }}</el-button
>
<el-button type="primary" v-if="row.use_time != 0" link @click="showOrder(row)">{{ t('showOrder') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
:page-sizes="[5,10,20,50,100]"
@size-change="couponCollection()"
@current-change="couponCollection"
/>
@size-change="couponCollection()" @current-change="couponCollection" />
</div>
</div>
</div>
</el-drawer>
</template>
@ -168,7 +107,7 @@ const showDialog = ref(false)
const loading = ref(false)
const repeat = ref(false)
let popTitle: string = '优惠券领取记录'
let couponId = ''
let couponId = '';
const route = useRoute()
const router = useRouter()
@ -178,7 +117,7 @@ const password_input = ref(true)
const password_copy_input = ref(true)
const handleClose = (done: () => void) => {
showDialog.value = false
showDialog.value = false;
}
const activeName = ref('order')
@ -192,8 +131,8 @@ const tableData = reactive({
loading: false,
data: [],
searchParam: {
keywords: '',
},
keywords: ''
}
})
const searchFormRef = ref<FormInstance>()
const couponStatistics = ref([])
@ -206,32 +145,28 @@ const couponCollection = () => {
page: tableData.page,
limit: tableData.limit,
id: couponId,
...tableData.searchParam,
})
.then((res) => {
...tableData.searchParam
}).then(res => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
console.log('tableData', tableData, couponId)
})
.catch(() => {
console.log("tableData",tableData,couponId);
}).catch(() => {
tableData.loading = false
})
//
getCouponInfo(couponId)
.then((res) => {
getCouponInfo(couponId).then(res => {
couponStatistics.value = res.data
loading.value = false
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
//
const showOrder = (data: any) => {
showDialog.value = false
showDialog.value = false;
router.push('/shop/order/detail?order_id=' + data.trade_id)
}
@ -242,15 +177,15 @@ const resetForm = (formEl: FormInstance | undefined) => {
}
const setFormData = async (row: any = null) => {
couponId = row.id
formData.value = null
activeName.value = 'order'
couponCollection()
couponId = row.id;
formData.value = null;
activeName.value = 'order';
couponCollection();
}
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>

34
admin/src/addon/shop/views/marketing/coupon/components/coupon-spread-popup.vue

@ -1,15 +1,9 @@
<template>
<div>
<el-dialog
v-model="showDialog"
:title="t('couponSpreadTitle')"
width="500px"
:destroy-on-close="true"
>
<el-dialog v-model="showDialog" :title="t('couponSpreadTitle')" width="500px" :destroy-on-close="true">
<div class="promote-flex flex">
<div
class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]"
>
<div class="promote-img flex justify-center items-center bg-[#f8f8f8] w-[150px] h-[150px]">
<el-image :src="wapImage" />
</div>
@ -17,16 +11,14 @@
<div class="mb-[10px]">{{ t('spreadLink') }}</div>
<el-input class="mb-[10px]" readonly :value="wapPreview">
<template #append>
<el-button @click="copyEvent(wapPreview)">{{
t('copy')
}}</el-button>
<el-button @click="copyEvent(wapPreview)">{{ t('copy') }}</el-button>
</template>
</el-input>
<a class="text-primary" :href="wapImage" download>{{
t('downloadQrcode')
}}</a>
<a class="text-primary" :href="wapImage" download>{{ t('downloadQrcode') }}</a>
</div>
</div>
</el-dialog>
</div>
</template>
@ -73,11 +65,7 @@ getUrl().then((res: any) => {
const loadQrcode = () => {
wapPreview.value = `${wapUrl.value}${page.value}`
// errorCorrectionLevelLH()
QRCode.toDataURL(wapPreview.value, {
errorCorrectionLevel: 'L',
margin: 0,
width: 120,
}).then((url) => {
QRCode.toDataURL(wapPreview.value, { errorCorrectionLevel: 'L', margin: 0, width: 120 }).then(url => {
wapImage.value = url
})
}
@ -95,7 +83,7 @@ const copyEvent = (text: string) => {
if (!isSupported.value) {
ElMessage({
message: t('notSupportCopy'),
type: 'warning',
type: 'warning'
})
}
copy(text)
@ -105,14 +93,14 @@ watch(copied, () => {
if (copied.value) {
ElMessage({
message: t('copySuccess'),
type: 'success',
type: 'success'
})
}
})
defineExpose({
showDialog,
show,
show
})
</script>

264
admin/src/addon/shop/views/marketing/coupon/edit.vue

@ -1,45 +1,22 @@
<template>
<div class="main-container">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<!-- 表单 -->
<el-card
class="box-card mt-[15px] !border-none"
shadow="never"
v-if="Object.keys(formData).length"
v-loading="loading"
>
<el-form
class="page-form"
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
>
<el-card class="box-card mt-[15px] !border-none " shadow="never" v-if="Object.keys(formData).length" v-loading="loading">
<el-form class="page-form" :model="formData" label-width="120px" ref="formRef" :rules="formRules">
<!-- 优惠券信息 -->
<!-- 优惠券名称 -->
<el-form-item :label="t('title')" prop="title">
<el-input
v-model.trim="formData.title"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
:maxlength="20"
/>
<el-input v-model.trim="formData.title" clearable :placeholder="t('titlePlaceholder')" class="input-width" :maxlength="20" />
</el-form-item>
<!-- 优惠券面额 -->
<el-form-item :label="t('price')" prop="price">
<el-input
v-model.trim="formData.price"
@keyup="filterDigit($event)"
clearable
:placeholder="t('pricePlaceholder')"
class="input-width"
maxlength="5"
/>
<el-input v-model.trim="formData.price" @keyup="filterDigit($event)" clearable :placeholder="t('pricePlaceholder')" class="input-width" maxlength="5" />
</el-form-item>
<!-- 优惠券类型 -->
@ -53,26 +30,14 @@
<el-form-item v-if="formData.type == 2" prop="goods_category_ids">
<div>
<el-cascader
v-model="formData.goods_category_ids"
:options="options"
:props="props"
collapse-tags
collapse-tags-tooltip
clearable
/>
<el-cascader v-model="formData.goods_category_ids" :options="options" :props="props" collapse-tags collapse-tags-tooltip clearable />
</div>
</el-form-item>
<el-form-item v-if="formData.type == 3" prop="goods_ids">
<div>
<el-form-item>
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="formData.goods_ids"
:min="1"
:max="99"
/>
<goods-select-popup ref="goodsSelectPopupRef" v-model="formData.goods_ids" :min="1" :max="99" />
</el-form-item>
</div>
</el-form-item>
@ -88,12 +53,7 @@
<el-form-item v-if="formData.threshold == 1" prop="min_condition_money">
最低满
<div class="flex items-center px-[5px]">
<el-input
v-model.trim="formData.min_condition_money"
@keyup="filterDigit($event)"
clearable
class="!w-[100px]"
/>
<el-input v-model.trim="formData.min_condition_money" @keyup="filterDigit($event)" clearable class="!w-[100px]" />
</div>
元可用
</el-form-item>
@ -109,12 +69,7 @@
<el-form-item v-show="formData.valid_type == 1">
领劵后立即生效有效期
<div class="flex items-center px-[5px]">
<el-input
v-model.trim="formData.length"
@keyup="filterNumber($event)"
clearable
class="!w-[100px]"
/>
<el-input v-model.trim="formData.length" @keyup="filterNumber($event)" clearable class="!w-[100px]" />
</div>
</el-form-item>
@ -122,11 +77,7 @@
<el-form-item prop="valid_time" v-if="formData.valid_type == 2">
领劵后立即生效使用时间截止至
<div class="w-[220px] pl-[5px]">
<el-date-picker
v-model="formData.valid_time"
type="datetime"
:placeholder="t('validTimePlaceholder')"
/>
<el-date-picker v-model="formData.valid_time" type="datetime" :placeholder="t('validTimePlaceholder')" />
</div>
</el-form-item>
@ -139,98 +90,58 @@
<el-radio :label="2">{{ t('grant') }}</el-radio>
</el-radio-group>
</div>
<div class="form-tip">
开启手动领取后会员可以直接在优惠券列表以及优惠券推广中直接领取
</div>
<div class="form-tip">开启手动领取后会员可以直接在优惠券列表以及优惠券推广中直接领取</div>
</el-form-item>
<!-- 领取时间 -->
<el-form-item
:label="t('receiveTime')"
v-show="formData.receive_type == 1"
>
<el-form-item :label="t('receiveTime')" v-show="formData.receive_type == 1">
<el-radio-group v-model="formData.receive_type_time">
<el-radio :label="1">{{ t('limitedTime') }}</el-radio>
<el-radio :label="2">{{ t('unlimitedTime') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
prop="receive_time"
v-show="formData.receive_type_time == 1 && formData.receive_type == 1"
>
<el-form-item prop="receive_time" v-show="formData.receive_type_time == 1 && formData.receive_type == 1">
<div class="w-[180px]">
<el-date-picker
v-model="formData.receive_time"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
<el-date-picker v-model="formData.receive_time" type="datetimerange" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期"/>
</div>
</el-form-item>
<!-- 优惠券数量 -->
<el-form-item
:label="t('receiveNumber')"
v-show="formData.receive_type == 1"
>
<el-form-item :label="t('receiveNumber')" v-show="formData.receive_type == 1">
<el-radio-group v-model="formData.limit">
<el-radio :label="1">{{ t('limit') }}</el-radio>
<el-radio :label="2">{{ t('unlimited') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-show="formData.limit == 1 && formData.receive_type == 1"
prop="remain_count"
>
<el-form-item v-show="formData.limit == 1 && formData.receive_type == 1" prop="remain_count">
<div>
<el-input
onkeypress="return( /[\d]/.test(String.fromCharCode(event.keyCode) ) )"
v-model.trim="formData.remain_count"
clearable
:placeholder="t('remainCountPlaceholder')"
class="input-width"
:min="formData.remain_count"
:max="100000"
:controls="false"
maxlength="6"
>
<el-input onkeypress='return( /[\d]/.test(String.fromCharCode(event.keyCode) ) )'
v-model.trim="formData.remain_count" clearable :placeholder="t('remainCountPlaceholder')"
class="input-width" :min="formData.remain_count" :max="100000" :controls="false" maxlength="6">
<template #append></template>
</el-input>
</div>
</el-form-item>
<!-- 用户可领取数量 -->
<el-form-item
:label="t('userLimitCount')"
prop="limit_count"
v-show="formData.receive_type == 1"
>
<el-input
onkeypress="return( /[\d]/.test(String.fromCharCode(event.keyCode) ) )"
v-model.trim="formData.limit_count"
clearable
:placeholder="t('userLimitCountPlaceholder')"
class="input-width"
:min="1"
:max="100000"
maxlength="6"
>
<el-form-item :label="t('userLimitCount')" prop="limit_count" v-show="formData.receive_type == 1">
<el-input onkeypress='return( /[\d]/.test(String.fromCharCode(event.keyCode) ) )'
v-model.trim="formData.limit_count" clearable :placeholder="t('userLimitCountPlaceholder')"
class="input-width" :min="1" :max="100000" maxlength="6">
<template #append></template>
</el-input>
<div class="form-tip">每个会员通过前端直接可领用数量</div>
</el-form-item>
</el-form>
</el-card>
<!-- 提交按钮 -->
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
@ -241,11 +152,7 @@
import { ref, computed, onMounted } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import {
getGoodsCategoryList,
editCoupon,
getCouponInfo,
} from '@/addon/shop/api/marketing'
import { getGoodsCategoryList, editCoupon, getCouponInfo } from '@/addon/shop/api/marketing'
import type { FormInstance } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import { filterNumber, filterDigit } from '@/utils/common'
@ -280,7 +187,7 @@ const initialFormData = {
receive_type_time: 2,
valid_type: 1, // 1=2=
receive_time: [start, end],
valid_time: end,
valid_time: end
}
const formData: Record<string, any> = ref({ ...initialFormData })
const formRef = ref<FormInstance>()
@ -289,41 +196,41 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' },
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' }
],
price: [
{ required: true, validator: priceRule, trigger: 'blur' }
],
goods_ids: [
{ required: true, message: t('请选择商品'), trigger: 'blur' }
],
price: [{ required: true, validator: priceRule, trigger: 'blur' }],
goods_ids: [{ required: true, message: t('请选择商品'), trigger: 'blur' }],
goods_category_ids: [
{ required: true, message: t('请选择商品分类'), trigger: 'blur' },
{ required: true, message: t('请选择商品分类'), trigger: 'blur' }
],
min_condition_money: [
{ required: true, validator: minConditionMoney, trigger: 'blur' },
{ required: true, validator: minConditionMoney, trigger: 'blur' }
],
valid_time: [{ required: true, validator: validTime, trigger: 'blur' }],
receive_time: [{ required: true, validator: receiveTime, trigger: 'blur' }],
remain_count: [{ required: true, validator: remainCount, trigger: 'blur' }],
limit_count: [
{ required: true, validator: limitCountRule, trigger: 'blur' },
valid_time: [
{ required: true, validator: validTime, trigger: 'blur' }
],
receive_time: [
{ required: true, validator: receiveTime, trigger: 'blur' }
],
remain_count: [
{ required: true, validator: remainCount, trigger: 'blur' }
],
limit_count: [
{ required: true, validator: limitCountRule, trigger: 'blur' }
]
}
})
const receiveTime = (rule: any, value: any, callback: any) => {
if (
formData.value.receive_type_time == 1 &&
formData.value.receive_type == 1
) {
if (
!formData.value.receive_time[0] ||
timestampFn(formData.value.receive_time[0]) <= Date.now()
) {
if (formData.value.receive_type_time == 1 && formData.value.receive_type == 1) {
if (!formData.value.receive_time[0] || timestampFn(formData.value.receive_time[0]) <= Date.now()) {
callback(new Error(t('领取开始时间不能小于等于当前时间')))
}
if (
!formData.value.receive_time[1] ||
timestampFn(formData.value.receive_time[1]) <=
timestampFn(formData.value.receive_time[0])
) {
if (!formData.value.receive_time[1] || timestampFn(formData.value.receive_time[1]) <= timestampFn(formData.value.receive_time[0])) {
callback(new Error(t('领取结束时间不能小于等于领取开始时间')))
}
}
@ -331,21 +238,11 @@ const receiveTime = (rule: any, value: any, callback: any) => {
}
const validTime = (rule: any, value: any, callback: any) => {
if (
formData.value.valid_type == 2 &&
formData.value.valid_time <= Date.now()
) {
if (formData.value.valid_type == 2 && formData.value.valid_time <= Date.now()) {
callback(new Error(t('有效期不能小于等于当前时间')))
}
if (
formData.value.valid_type == 2 &&
formData.value.receive_type_time == 1 &&
formData.value.receive_type == 1
) {
if (
timestampFn(formData.value.valid_time) <=
timestampFn(formData.value.receive_time[1])
) {
if(formData.value.valid_type == 2 && formData.value.receive_type_time == 1 && formData.value.receive_type == 1){
if (timestampFn(formData.value.valid_time) <= timestampFn(formData.value.receive_time[1])) {
callback(new Error(t('有效期不能小于等于领取结束时间')))
}
}
@ -353,54 +250,33 @@ const validTime = (rule: any, value: any, callback: any) => {
}
const minConditionMoney = (rule: any, value: any, callback: any) => {
if (
formData.value.threshold == 1 &&
formData.value.min_condition_money <= 0
) {
if (formData.value.threshold == 1 && formData.value.min_condition_money <= 0) {
callback(new Error(t('使用门槛最低不能小于0元')))
}
callback()
}
const remainCount = (rule: any, value: any, callback: any) => {
if (
formData.value.remain_count != '' &&
formData.value.remain_count > 100000
) {
if (formData.value.remain_count != '' && formData.value.remain_count > 100000) {
callback(new Error(t('remainCountPlaceholder')))
}
if (
!formData.value.remain_count ||
(formData.value.remain_count != '' && formData.value.remain_count < 1)
) {
if (!formData.value.remain_count || formData.value.remain_count != '' && formData.value.remain_count < 1) {
callback(new Error(t('发放数量不能小于1张')))
}
callback()
}
const priceRule = (rule: any, value: any, callback: any) => {
if (
!formData.value.price ||
formData.value.price == '' ||
formData.value.price <= 0
) {
if (!formData.value.price || formData.value.price == '' || formData.value.price <= 0) {
callback(new Error(t('pricePlaceholder')))
}
callback()
}
const limitCountRule = (rule: any, value: any, callback: any) => {
if (
!formData.value.limit_count ||
(formData.value.limit_count != '' && formData.value.limit_count < 1)
) {
if (!formData.value.limit_count || formData.value.limit_count != '' && formData.value.limit_count < 1) {
callback(new Error(t('userLimitCountPlaceholder')))
}
if (
formData.value.limit == 1 &&
formData.value.limit_count != '' &&
formData.value.remain_count != '' &&
parseInt(formData.value.limit_count) > parseInt(formData.value.remain_count)
) {
if (formData.value.limit == 1 && formData.value.limit_count != '' && formData.value.remain_count != '' && parseInt(formData.value.limit_count) > parseInt(formData.value.remain_count)) {
callback(new Error(t('限领张数不能大于发放数量')))
}
callback()
@ -429,12 +305,10 @@ const onSave = async (formEl: FormInstance | undefined) => {
delete data.goods_category_ids
}
const save = editCoupon
save(data)
.then((res) => {
save(data).then(res => {
loading.value = false
history.back()
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
@ -454,28 +328,22 @@ const timestampFn = (data) => {
//
const getCouponInfoFn = (id: any) => {
loading.value = true
getCouponInfo(id)
.then((res) => {
getCouponInfo(id).then(res => {
formData.value = Object.assign(formData.value, res.data)
if (
parseInt(formData.value.start_time) != 0 &&
parseInt(formData.value.end_time) != 0
) {
if (parseInt(formData.value.start_time) != 0 && parseInt(formData.value.end_time) != 0) {
const start_time = new Date(formData.value.start_time)
const end_time = new Date(formData.value.end_time)
formData.value.receive_type_time = 1
formData.value.receive_time = [start_time, end_time]
}
if (res.data.valid_end_time)
formData.value.valid_time = res.data.valid_end_time
if (res.data.valid_end_time) formData.value.valid_time = res.data.valid_end_time
if (formData.value.type == 2) {
goodsCategoryFormatting(formData.value.goods_category_ids)
}
loading.value = false
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
@ -517,7 +385,7 @@ input::-webkit-inner-spin-button {
margin: 0;
}
input[type='number'] {
input[type="number"] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
-o-appearance: textfield;

187
admin/src/addon/shop/views/marketing/coupon/list.vue

@ -2,6 +2,7 @@
<div class="main-container">
<!-- 添加优惠券按钮 -->
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="handleChange">
@ -10,53 +11,26 @@
</div>
<!-- 搜索 -->
<el-card
class="box-card !border-none my-[20px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="tableData.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[20px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('title')" prop="title">
<el-input
v-model.trim="tableData.searchParam.title"
:placeholder="t('titlePlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.title" :placeholder="t('titlePlaceholder')" />
</el-form-item>
<el-form-item :label="t('statusName')" prop="status">
<el-select
v-model="tableData.searchParam.status"
clearable
class="input-item"
>
<el-option
v-for="(item, index) in statusList"
:key="index"
:label="item"
:value="index"
></el-option>
<el-form-item :label="t('statusName')" prop='status'>
<el-select v-model="tableData.searchParam.status" clearable class="input-item">
<el-option v-for="(item, index) in statusList" :key="index" :label="item" :value="index"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadCouponList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadCouponList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 列表 -->
<div class="mt-[10px]">
<el-table
:data="tableData.data"
size="large"
v-loading="tableData.loading"
>
<el-table :data="tableData.data" size="large" v-loading="tableData.loading">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
@ -78,17 +52,13 @@
<el-table-column :label="t('sumCount')" min-width="160">
<template #default="{ row }">
<span v-if="row.receive_type == 1 && row.sum_count != '-1'"
>{{ row.remain_count || 0 }} / {{ row.sum_count || 0 }}</span
>
<span v-if="row.receive_type == 1 && row.sum_count != '-1'">{{ row.remain_count || 0 }} / {{ row.sum_count || 0 }}</span>
<span v-else>不限量</span>
</template>
</el-table-column>
<el-table-column :label="t('remainCount')" min-width="140">
<template #default="{ row }">
<span
>{{ row.receive_count || 0 }} / {{ row.receive_use_count || 0 }}
</span>
<span >{{ row.receive_count || 0}} / {{ row.receive_use_count || 0}} </span>
</template>
</el-table-column>
<el-table-column :label="t('threshold')" min-width="130" >
@ -100,12 +70,8 @@
<el-table-column :label="t('validType')" min-width="190">
<template #default="{ row }">
<template v-if="row.receive_type == 1">
<span v-if="row.valid_type == 1">
领取之日起{{ row.length || '' }} 天内有效</span
>
<span v-else>
使用截止时间至{{ row.valid_end_time || '' }}
</span>
<span v-if="row.valid_type == 1"> 领取之日起{{ row.length || '' }} 天内有效</span>
<span v-else> 使用截止时间至{{ row.valid_end_time || ''}} </span>
</template>
<span v-else>--</span>
</template>
@ -113,12 +79,7 @@
<el-table-column :label="t('receiveTypeTime')" min-width="210">
<template #default="{ row }">
<div v-if="row.receive_type == 1">
<div
v-if="
parseInt(row.start_time) != 0 && parseInt(row.end_time) != 0
"
class="flex flex-col"
>
<div v-if="parseInt(row.start_time) != 0 && parseInt(row.end_time) !=0" class="flex flex-col">
<span>开始时间{{row.start_time}}</span>
<span>结束时间{{row.end_time}}</span>
</div>
@ -127,57 +88,21 @@
<span v-else>--</span>
</template>
</el-table-column>
<el-table-column
prop="status_name"
:label="t('statusName')"
min-width="98"
/>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="160"
>
<el-table-column prop="status_name" :label="t('statusName')" min-width="98" />
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="160">
<template #default="{ row }">
<el-button type="primary" link @click="spreadEvent(row)">{{
t('spreadGoods')
}}</el-button>
<el-button
type="primary"
link
@click="editEvent(row)"
v-if="row.status == 0 || row.status == 1"
>{{ t('edit') }}</el-button
>
<el-button
type="primary"
link
@click="deleteEvent(row)"
v-if="row.status != 1"
>{{ t('delete') }}</el-button
>
<el-button
type="primary"
link
@click="closeEvent(row)"
v-if="row.status == 1"
>{{ t('close') }}</el-button
>
<el-button type="primary" link @click="collectionEvent(row)">{{
t('receive')
}}</el-button>
<el-button type="primary" link @click="spreadEvent(row)">{{ t('spreadGoods') }}</el-button>
<el-button type="primary" link @click="editEvent(row)" v-if="row.status == 0 || row.status == 1">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="deleteEvent(row)" v-if="row.status != 1">{{ t('delete') }}</el-button>
<el-button type="primary" link @click="closeEvent(row)" v-if="row.status == 1">{{ t('close') }}</el-button>
<el-button type="primary" link @click="collectionEvent(row)">{{ t('receive') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="loadCouponList()"
@current-change="loadCouponList"
/>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
@size-change="loadCouponList()" @current-change="loadCouponList" />
</div>
</div>
</el-card>
@ -190,16 +115,11 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {
getCouponList,
deleteCoupon,
closeCoupon,
getCouponStatusList,
} from '@/addon/shop/api/marketing'
import { getCouponList, deleteCoupon, closeCoupon,getCouponStatusList } from '@/addon/shop/api/marketing'
import { ElMessageBox, FormInstance } from 'element-plus'
import { t } from '@/lang'
import couponSpreadPopup from '@/addon/shop/views/marketing/coupon/components/coupon-spread-popup.vue'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
import couponCollection from '@/addon/shop/views/marketing/coupon/components/coupon-collection.vue'
const router = useRouter()
@ -216,13 +136,13 @@ const tableData = reactive({
searchParam: {
title: '',
status:'',
},
}
})
const searchFormRef = ref<FormInstance>()
const getCouponStatusListFn = () =>{
getCouponStatusList().then((res) => {
getCouponStatusList().then(res=>{
statusList.value = res.data
})
}
@ -234,23 +154,17 @@ const loadCouponList = (page: number = 1) => {
getCouponList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
})
.then((res) => {
...tableData.searchParam
}).then(res => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
setTablePageStorage(
tableData.page,
tableData.limit,
tableData.searchParam
)
})
.catch(() => {
setTablePageStorage(tableData.page, tableData.limit, tableData.searchParam);
}).catch(() => {
tableData.loading = false
})
}
loadCouponList(getTablePageStorage(tableData.searchParam).page)
loadCouponList(getTablePageStorage(tableData.searchParam).page);
//
const couponCollectionRef: any = ref(null)
@ -272,40 +186,42 @@ const editEvent = (data: any) => {
//
const collectionEvent = (data: any) => {
let parameter = { id: data.id }
couponCollectionRef.value.setFormData(parameter)
couponCollectionRef.value.showDialog = true
let parameter = {id: data.id};
couponCollectionRef.value.setFormData(parameter);
couponCollectionRef.value.showDialog = true;
}
/**
* 删除
*/
const deleteEvent = (data: any) => {
ElMessageBox.confirm(t('couponDeleteTips'), t('warning'), {
ElMessageBox.confirm(t('couponDeleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteCoupon(data.id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteCoupon(data.id).then(() => {
loadCouponList()
}).catch(() => {
})
.catch(() => {})
})
}
//
const closeEvent = (data: any) => {
ElMessageBox.confirm(t('couponColseTips'), t('warning'), {
ElMessageBox.confirm(t('couponColseTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
closeCoupon(data.id)
.then(() => {
type: 'warning'
}
).then(() => {
closeCoupon(data.id).then(() => {
loadCouponList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -314,6 +230,7 @@ const resetForm = (formEl: FormInstance | undefined) => {
formEl.resetFields()
loadCouponList()
}
</script>
<style lang="scss" scoped></style>

569
admin/src/addon/shop/views/marketing/discount/add.vue

@ -1,123 +1,62 @@
<template>
<div class="main-container">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<!-- 表单 -->
<el-card
class="box-card mt-[15px] !border-none"
shadow="never"
v-loading="loading"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
>
<el-card class="box-card mt-[15px] !border-none" shadow="never" v-loading="loading">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form">
<!-- 活动名称 -->
<el-form-item :label="t('name')" prop="active_name">
<div>
<el-input
v-model.trim="formData.active_name"
clearable
:placeholder="t('namePlaceholder')"
class="input-width"
:maxlength="20"
/>
<el-input v-model.trim="formData.active_name" clearable :placeholder="t('namePlaceholder')" class="input-width" :maxlength="20" />
<p class=" text-[14px] text-[#999]">{{ t('nameTip') }}</p>
</div>
</el-form-item>
<!-- 活动标题 -->
<el-form-item :label="t('title')" prop="active_desc">
<div>
<el-input
v-model.trim="formData.active_desc"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
:maxlength="20"
/>
<el-input v-model.trim="formData.active_desc" clearable :placeholder="t('titlePlaceholder')" class="input-width" :maxlength="20" />
<p class=" text-[14px] text-[#999]">{{ t('titleTip') }}</p>
</div>
</el-form-item>
<!-- 活动时间 -->
<el-form-item :label="t('activityTime')" prop="discount_time">
<div class="w-[180px]">
<el-date-picker
v-model="formData.discount_time"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
<el-date-picker v-model="formData.discount_time" type="datetimerange" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期"/>
</div>
</el-form-item>
<!-- 选择商品 -->
<el-form-item :label="t('selectProduct')" class="!m-0">
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="formData.goods_ids"
@goodsSelect="goodsSelect"
:min="1"
:max="99"
/>
<goods-select-popup ref="goodsSelectPopupRef" v-model="formData.goods_ids" @goodsSelect="goodsSelect" :min="1" :max="99" />
</el-form-item>
<el-form-item prop="goods_list"></el-form-item>
<el-form-item v-if="formData.goods_list.length&& goodsTable.data">
<div class="w-full sku_list">
<el-table
class="!w-[1400px] !max-w-[100%]"
:data="goodsTable.list"
size="large"
ref="goods_listTableRef"
@selection-change="handleSelectionChange"
>
<el-table class="!w-[1400px] !max-w-[100%]" :data="goodsTable.list" size="large" ref="goods_listTableRef" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ t('emptyData')}}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
:label="t('goodsSelectPopupGoodsInfo')"
min-width="300"
>
<el-table-column :label="t('goodsSelectPopupGoodsInfo')" min-width="300">
<template #default="{ row }">
<div class="flex items-center cursor-pointer">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.goods_cover_thumb_small"
class="w-[60px] h-[60px]"
:src="img(row.goods_cover_thumb_small)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.goods_cover_thumb_small" class="w-[60px] h-[60px]" :src="img(row.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.goods_name" class="multi-hidden">{{
row.goods_name
}}</span>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
<span :title="row.goods_name" class="multi-hidden">{{ row.goods_name }}</span>
<span class="text-primary text-[12px]">{{ row.goods_type_name }}</span>
</div>
</div>
</template>
@ -130,230 +69,110 @@
<el-table-column :label="t('discounts')" width="170">
<template #default="{ row,$index }">
<el-form-item
v-if="!row.goodsSku.sku_spec_format"
:key="row.goods_id"
:prop="'goods_list.' + row.index + '.discount_rate'"
:rules="[
{
<el-form-item v-if="!row.goodsSku.sku_spec_format" :key="row.goods_id" :prop="'goods_list.'+row.index + '.discount_rate'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
callback(t('discountsPlaceholder'))
} else if (
isNaN(value) ||
!regExp.number.test(value)
) {
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('discountsTips'))
} else if (value <= 0) {
callback(t('discountsTipsTwo'))
} else if (value>9.9) {
callback(t('discountsTipsThree'))
} else {
callback()
callback();
}
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.discount_rate"
@blur="inputBlur(row, 'discount', row.index)"
clearable
placeholder="0.00"
maxlength="8"
/>
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.discount_rate" @blur="inputBlur(row,'discount',row.index)" clearable placeholder="0.00" maxlength="8" />
</el-form-item>
<el-form-item
:prop="'goods_list.' + row.index + '.valid'"
:rules="[
{
<el-form-item :prop="'goods_list.'+row.index + '.valid'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(t('skuDiscountSettingsPlaceholder'))
} else {
callback()
callback();
}
}
},
},
]"
v-else
>
<span
v-if="
row.valid &&
row.min_discount_rate != Infinity &&
row.max_discount_rate != -Infinity
"
>{{
row.min_discount_rate == row.max_discount_rate
? row.min_discount_rate
: row.min_discount_rate + '-' + row.max_discount_rate
}}</span
>
}]" v-else>
<span v-if="row.valid && row.min_discount_rate!=Infinity&&row.max_discount_rate!=-Infinity">{{ row.min_discount_rate==row.max_discount_rate?row.min_discount_rate:row.min_discount_rate+'-'+row.max_discount_rate }}</span>
<span v-else>--</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column :label="t('reduceMoney')" width="170">
<template #default="{ row,$index }">
<el-form-item
v-if="!row.goodsSku.sku_spec_format"
:key="row.goods_id"
:prop="'goods_list.' + row.index + '.reduce_money'"
:rules="[
{
<el-form-item v-if="!row.goodsSku.sku_spec_format" :key="row.goods_id" :prop="'goods_list.'+row.index + '.reduce_money'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
callback(t('reduceMoneyPlaceholder'))
} else if (
isNaN(value) ||
!regExp.digit.test(value)
) {
} else if (isNaN(value) || !regExp.digit.test(value)) {
callback(t('reduceMoneyTips'))
} else if (value <=0) {
callback(t('reduceMoneyTipsTwo'))
} else if (value >= parseFloat(row.goodsSku.price) ) {
callback(t('reduceMoneyTipsThree'))
} else {
callback()
callback();
}
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.reduce_money"
@blur="inputBlur(row, 'reduce', row.index)"
clearable
placeholder="0.00"
maxlength="8"
/>
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.reduce_money" @blur="inputBlur(row,'reduce',row.index)" clearable placeholder="0.00" maxlength="8" />
</el-form-item>
<el-form-item v-else>
<span
v-if="
row.valid &&
row.min_reduce_money != Infinity &&
row.max_reduce_money != -Infinity
"
>{{
row.min_reduce_money == row.max_reduce_money
? row.min_reduce_money
: row.min_reduce_money + '-' + row.max_reduce_money
}}</span
>
<span v-if="row.valid && row.min_reduce_money!=Infinity&&row.max_reduce_money!=-Infinity">{{ row.min_reduce_money==row.max_reduce_money?row.min_reduce_money:row.min_reduce_money+'-'+row.max_reduce_money }}</span>
<span v-else>--</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column :label="t('promotional')" width="170">
<template #default="{ row,$index }">
<el-form-item
v-if="!row.goodsSku.sku_spec_format"
:key="row.goods_id"
:prop="'goods_list.' + row.index + '.specify_price'"
:rules="[
{
<el-form-item v-if="!row.goodsSku.sku_spec_format" :key="row.goods_id" :prop="'goods_list.'+row.index + '.specify_price'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
callback(t('promotionalPlaceholder'))
} else if (
isNaN(value) ||
!regExp.digit.test(value)
) {
} else if (isNaN(value) || !regExp.digit.test(value)) {
callback(t('promotionalTips'))
} else if (value <=0 ) {
callback(t('promotionalTipsTwo'))
}else if (value >= parseFloat(row.goodsSku.price) ) {
callback(t('promotionalTipsThree'))
} else {
callback()
callback();
}
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.specify_price"
clearable
@blur="inputBlur(row, 'specify', row.index)"
placeholder="0.00"
maxlength="8"
/>
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.specify_price" clearable @blur="inputBlur(row,'specify',row.index)" placeholder="0.00" maxlength="8" />
</el-form-item>
<el-form-item v-else>
<span
v-if="
row.valid &&
row.min_specify_price != Infinity &&
row.max_specify_price != -Infinity
"
>{{
row.min_specify_price == row.max_specify_price
? row.min_specify_price
: row.min_specify_price + '-' + row.max_specify_price
}}</span
>
<span v-if="row.valid && row.min_specify_price!=Infinity&&row.max_specify_price!=-Infinity">{{ row.min_specify_price==row.max_specify_price?row.min_specify_price:row.min_specify_price+'-'+row.max_specify_price }}</span>
<span v-else>--</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column :label="t('discountType')" width="130">
<template #default="{ row }">
<span v-if="!row.goodsSku.sku_spec_format">{{
row.discount_type == 'discount'
? t('discounts')
: row.discount_type == 'reduce'
? t('reduceMoney')
: t('promotional')
}}</span>
<span v-if="!row.goodsSku.sku_spec_format">{{row.discount_type=='discount'?t('discounts'):row.discount_type=='reduce'?t('reduceMoney'):t('promotional')}}</span>
<el-form-item v-else>请在设置中查看</el-form-item>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
align="right"
min-width="160"
>
<el-table-column :label="t('operation')" align="right" min-width="160">
<template #default="{row}">
<!-- <el-button type="primary" link @click="enabledEvent(row)">{{ row.is_enabled?t('noEnabled'):t('enabled') }}</el-button> -->
<el-button
v-if="row.goodsSku.sku_spec_format"
type="primary"
link
@click="
skuDiscountSettingsEvent(formData.goods_list[row.index])
"
>
<el-button v-if="row.goodsSku.sku_spec_format" type="primary" link @click="skuDiscountSettingsEvent(formData.goods_list[row.index])">
{{t('skuDiscountSettings') }}
</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.index)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="deleteEvent(row.index)">{{t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div
class="flex items-center justify-between mt-[15px] !w-[1400px] !max-w-[100%]"
>
<div class="flex items-center justify-between mt-[15px] !w-[1400px] !max-w-[100%]">
<div class="flex items-center mb-[15px]">
<el-checkbox
v-model="toggleCheckbox"
size="large"
class="!mr-[15px]"
@change="toggleChange"
:indeterminate="isIndeterminate"
>
<el-checkbox v-model="toggleCheckbox" size="large" class="!mr-[15px]" @change="toggleChange" :indeterminate="isIndeterminate">
<span>已选 {{ multipleSelection.length }} </span>
</el-checkbox>
@ -363,30 +182,15 @@
<el-option :label="t('reduceMoney')" value="reduce" />
<el-option :label="t('promotional')" value="specify" />
</el-select> -->
<el-input
v-model.trim="batchOperation.discountNumber"
clearable
:placeholder="
batchOperation.discount_type == 'discount'
? t('discounts')
: batchOperation.discount_type == 'reduce'
? t('reduceMoney')
: t('promotional')
"
class="!w-[130px] ml-[10px]"
maxlength="8"
/>
<el-button class="ml-[10px]" type="primary" @click="saveBatch"
>{{ t('confirm') }}
<el-input v-model.trim="batchOperation.discountNumber" clearable
:placeholder="batchOperation.discount_type=='discount'?t('discounts'):batchOperation.discount_type=='reduce'?t('reduceMoney'):t('promotional')"
class="!w-[130px] ml-[10px]" maxlength="8" />
<el-button class="ml-[10px]" type="primary" @click="saveBatch">{{ t('confirm') }}
</el-button>
</div>
<el-pagination
v-model:current-page="goodsTable.page"
v-model:page-size="goodsTable.limit"
layout="total, prev, pager, next, jumper"
:total="goodsTable.total"
@current-change="setGoodsList"
/>
<el-pagination v-model:current-page="goodsTable.page" v-model:page-size="goodsTable.limit"
layout="total, prev, pager, next, jumper" :total="goodsTable.total"
@current-change="setGoodsList" />
</div>
</div>
</el-form-item>
@ -398,9 +202,7 @@
<!-- 提交按钮 -->
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
@ -411,7 +213,7 @@
import {ref, computed, reactive, nextTick} from 'vue'
import {t} from '@/lang'
import {useRoute, useRouter} from 'vue-router'
import { addActiveDiscount } from '@/addon/shop/api/marketing'
import {addActiveDiscount} from "@/addon/shop/api/marketing";
import {FormInstance, ElMessage} from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import {deepClone, img} from '@/utils/common'
@ -446,54 +248,43 @@ const formRef = ref<FormInstance>()
//
const regExp = {
number: /^\d{0,10}(.?\d{0,1})$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
digit: /^\d{0,10}(.?\d{0,2})$/
}
const formRules = computed(() => {
return {
active_name: [
{required: true, message: t('namePlaceholder'), trigger: 'blur'},
{ validator: noSpaceValidator, trigger: 'blur' },
{ validator: noSpaceValidator, trigger: 'blur' }
],
active_desc: [
{required: true, message: t('titlePlaceholder'), trigger: 'blur'},
{ validator: noSpaceValidator, trigger: 'blur' },
{ validator: noSpaceValidator, trigger: 'blur' }
],
goods_list: [
{
required: true,
message: t('selectProductPlaceholder'),
trigger: 'change',
},
{required: true, message: t('selectProductPlaceholder'), trigger: 'change'}
],
discount_time: [
{ required: true, validator: receiveTime, trigger: 'change' },
{required: true, validator: receiveTime, trigger: 'change'}
],
}
})
const noSpaceValidator = (rule: any, value: any, callback: any)=>{
if (value.trim() === '') {
return callback(new Error(t('noSpaceAllowed')))
return callback(new Error(t('noSpaceAllowed')));
}
callback() //
callback(); //
}
const receiveTime = (rule: any, value: any, callback: any) => {
if (
!formData.value.discount_time ||
(formData.value.discount_time &&
!formData.value.discount_time[0] &&
!formData.value.discount_time[1])
) {
if (!formData.value.discount_time || (formData.value.discount_time && !formData.value.discount_time[0] && !formData.value.discount_time[1])) {
callback(new Error(t('请选择活动时间')))
} else if (!formData.value.discount_time[0]) {
callback(new Error(t('请选择活动开始时间')))
} else if (!formData.value.discount_time[1]) {
callback(new Error(t('请选择活动结束时间')))
} else if (
formData.value.discount_time[1] <= formData.value.discount_time[0]
) {
} else if (formData.value.discount_time[1] <= formData.value.discount_time[0]) {
callback(new Error(t('活动结束时间不能小于等于活动开始时间')))
}
callback()
@ -502,10 +293,7 @@ const receiveTime = (rule: any, value: any, callback: any) => {
const validFn = (row: any) => {
if (row.discount_rate.length == 0) {
return false
} else if (
isNaN(row.discount_rate) ||
!regExp.number.test(row.discount_rate)
) {
} else if (isNaN(row.discount_rate) || !regExp.number.test(row.discount_rate)) {
return false
} else if (row.discount_rate <= 0) {
return false
@ -521,10 +309,7 @@ const validFn = (row: any) => {
return false
} else if (row.specify_price.length == 0) {
return false
} else if (
isNaN(row.specify_price) ||
!regExp.digit.test(row.specify_price)
) {
} else if (isNaN(row.specify_price) || !regExp.digit.test(row.specify_price)) {
return false
} else if (row.specify_price <= 0) {
return false
@ -541,23 +326,19 @@ const onSave = (formEl: FormInstance | undefined) => {
let el = formData.value.goods_list[i]
if (el.goodsSku.sku_spec_format) {
if (!el.valid) {
let page = Math.ceil(
i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit
)
let page = Math.ceil(i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit)
goodsTable.list = goodsTable.data[page - 1]
goodsTable.page = page
break
break;
} else {
el.sku_list = el.skuList
}
} else {
if (!validFn(el)) {
let page = Math.ceil(
i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit
)
let page = Math.ceil(i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit)
goodsTable.list = goodsTable.data[page - 1]
goodsTable.page = page
break
break;
} else {
el.skuList[0].discount_rate = el.discount_rate
el.skuList[0].reduce_money = el.reduce_money
@ -576,25 +357,24 @@ const onSave = (formEl: FormInstance | undefined) => {
formData.value.start_time = formData.value.discount_time[0]
formData.value.end_time = formData.value.discount_time[1]
formData.value.goods_data = JSON.stringify(formData.value.goods_list)
addActiveDiscount(formData.value)
.then((res) => {
addActiveDiscount(formData.value).then(res => {
loading.value = false
history.back()
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
})
})
}
interface goodsTableInterface {
page: number
limit: number
total: number
data: any
list: Array<any>
page: number,
limit: number,
total: number,
data: any,
list: Array<any>,
}
const goodsTable = reactive<goodsTableInterface>({
@ -602,13 +382,12 @@ const goodsTable = reactive<goodsTableInterface>({
limit: 5,
total: 0,
data: [],
list: [],
list: []
})
const goodsSelect = (value: any) => {
if (formData.value.goods_list.length) {
let goods_list = deepClone(Object.values(value)).map(
(el: any, index: number) => {
let goods_list = deepClone(Object.values(value)).map((el: any, index: number) => {
if (!el.goodsSku.sku_spec_format) {
el.discount_type = 'discount'
el.discount_rate = ''
@ -625,14 +404,13 @@ const goodsSelect = (value: any) => {
el = Object.assign(el, v)//
el.index = index
}
})
return el
}
)
})
formData.value.goods_list = goods_list
} else {
formData.value.goods_list = deepClone(Object.values(value)).map(
(el: any, index: number) => {
formData.value.goods_list = deepClone(Object.values(value)).map((el: any, index: number) => {
if (!el.goodsSku.sku_spec_format) {
el.discount_type = 'discount'
el.discount_rate = ''
@ -645,8 +423,7 @@ const goodsSelect = (value: any) => {
el.index = index
el.valid = false
return el
}
)
})
}
setGoodsList()
if(formRef.value) formRef.value.validateField('goods_list').catch(()=>{})
@ -683,11 +460,11 @@ const setGoodsList = (page = 1) => {
//
const splitArray = (array: [], size: number) => {
var result = []
var result = [];
for (var i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size))
result.push(array.slice(i, i + size));
}
return result
return result;
}
//
@ -715,13 +492,13 @@ const skuSave = (row: any) => {
/*****批量设置 ****/
interface batchOperationInterface {
discount_type: any
discountNumber: any
discount_type: any,
discountNumber: any,
}
const batchOperation = ref<batchOperationInterface>({
discount_type: 'discount',
discountNumber: '',
discountNumber: ''
})
//
@ -745,10 +522,7 @@ const handleSelectionChange = (val: []) => {
multipleSelection.value = val
toggleCheckbox.value = false
if (
multipleSelection.value.length > 0 &&
multipleSelection.value.length < goodsTable.list.length
) {
if (multipleSelection.value.length > 0 && multipleSelection.value.length < goodsTable.list.length) {
isIndeterminate.value = true
} else {
isIndeterminate.value = false
@ -763,7 +537,7 @@ const saveBatch = () => {
if (!multipleSelection.value.length) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`,
message: `${t('batchEmptySelectedGoodsTips')}`
})
return
}
@ -771,28 +545,25 @@ const saveBatch = () => {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('discountsPlaceholder')}`,
message: `${t('discountsPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.number.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.number.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('discountsTips')}`,
message: `${t('discountsTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('discountsTipsTwo')}`,
message: `${t('discountsTipsTwo')}`
})
return
} else if (batchOperation.value.discountNumber > 9.9) {
ElMessage({
type: 'warning',
message: `${t('discountsTipsThree')}`,
message: `${t('discountsTipsThree')}`
})
return
}
@ -800,85 +571,68 @@ const saveBatch = () => {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyPlaceholder')}`,
message: `${t('reduceMoneyPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.digit.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.digit.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyTips')}`,
message: `${t('reduceMoneyTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyTipsTwo')}`,
message: `${t('reduceMoneyTipsTwo')}`
})
return
}
} else {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('promotionalPlaceholder')}`,
message: `${t('promotionalPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.digit.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.digit.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('promotionalTips')}`,
message: `${t('promotionalTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('promotionalTipsTwo')}`,
message: `${t('promotionalTipsTwo')}`
})
return
}
}
formData.value.goods_list.forEach((el: any, index: number) => {
multipleSelection.value.forEach((v: any) => {
if (v.goods_id === el.goods_id) {
if (!el.goodsSku.sku_spec_format) {
if (batchOperation.value.discount_type == 'discount') {
//
el.discount_rate = batchOperation.value.discountNumber + ''
//
el.specify_price = (
el.goodsSku.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
el.discount_price = (
el.goodsSku.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
el.specify_price = (el.goodsSku.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
el.discount_price = (el.goodsSku.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
//
el.reduce_money = (el.goodsSku.price - el.specify_price).toFixed(2)
} else if (batchOperation.value.discount_type == 'reduce') {
//
} else if (batchOperation.value.discount_type == 'reduce') {//
el.reduce_money = batchOperation.value.discountNumber + ''
el.specify_price = el.goodsSku.price - el.reduce_money.toFixed(2)
el.discount_price = (el.goodsSku.price - el.reduce_money).toFixed(2)
el.discount_rate = (
(el.specify_price / el.goodsSku.price) *
10
).toFixed(1)
} else {
//
el.discount_rate = (el.specify_price / el.goodsSku.price * 10).toFixed(1)
} else {//
el.specify_price = batchOperation.value.discountNumber + ''
el.discount_price = batchOperation.value.discountNumber + ''
el.reduce_money = (el.goodsSku.price - el.specify_price).toFixed(2)
el.discount_rate = (
(el.specify_price / el.goodsSku.price) *
10
).toFixed(1)
el.discount_rate = (el.specify_price / el.goodsSku.price * 10).toFixed(1)
}
el.discount_type = batchOperation.value.discount_type + ''
} else {
@ -888,47 +642,28 @@ const saveBatch = () => {
//
sku.discount_rate = batchOperation.value.discountNumber + ''
//
sku.specify_price = (
sku.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
sku.discount_price = (
sku.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
sku.specify_price = (sku.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
sku.discount_price = (sku.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
//
sku.reduce_money = (sku.price - sku.specify_price).toFixed(2)
} else if (batchOperation.value.discount_type == 'reduce') {
//
} else if (batchOperation.value.discount_type == 'reduce') {//
sku.reduce_money = batchOperation.value.discountNumber + ''
sku.specify_price = sku.price - sku.reduce_money.toFixed(2)
sku.discount_price = (sku.price - sku.reduce_money).toFixed(2)
sku.discount_rate = (
(sku.specify_price / sku.price) *
10
).toFixed(1)
} else {
//
sku.discount_rate = (sku.specify_price / sku.price * 10).toFixed(1)
} else {//
sku.specify_price = batchOperation.value.discountNumber + ''
sku.discount_price = batchOperation.value.discountNumber + ''
sku.reduce_money = (sku.price - sku.specify_price).toFixed(2)
sku.discount_rate = (
(sku.specify_price / sku.price) *
10
).toFixed(1)
sku.discount_rate = (sku.specify_price / sku.price * 10).toFixed(1)
}
sku.discount_type = batchOperation.value.discount_type + ''
}
})
let discount_rate_list = el.skuList
.filter((sku: any) => sku.is_enabled === 1)
.map((sku: any) => Number(sku.discount_rate))
let reduce_money_list = el.skuList
.filter((sku: any) => sku.is_enabled === 1)
.map((sku: any) => Number(sku.reduce_money))
let specify_price_list = el.skuList
.filter((sku: any) => sku.is_enabled === 1)
.map((sku: any) => Number(sku.specify_price))
let discount_rate_list = el.skuList.filter((sku:any)=>sku.is_enabled===1).map((sku:any)=>Number(sku.discount_rate))
let reduce_money_list = el.skuList.filter((sku:any)=>sku.is_enabled===1).map((sku:any)=>Number(sku.reduce_money))
let specify_price_list = el.skuList.filter((sku:any)=>sku.is_enabled===1).map((sku:any)=>Number(sku.specify_price))
el.max_discount_rate = Math.max(...discount_rate_list)
el.min_discount_rate = Math.min(...discount_rate_list)
el.max_reduce_money = Math.max(...reduce_money_list)
@ -951,63 +686,40 @@ const inputBlur = (row: any, discount_type: string, index: number) => {
if (discount_type == 'discount') {
if (row.discount_rate.length) {
//
row.specify_price = (
row.goodsSku.price *
(row.discount_rate / 10)
).toFixed(2)
row.discount_price = (
row.goodsSku.price *
(row.discount_rate / 10)
).toFixed(2)
row.specify_price = (row.goodsSku.price * (row.discount_rate / 10)).toFixed(2)
row.discount_price = (row.goodsSku.price * (row.discount_rate / 10)).toFixed(2)
//
row.reduce_money = (row.goodsSku.price - row.specify_price).toFixed(2)
if (formRef.value) {
formRef.value
.validateField('goods_list.' + index + '.specify_price')
.catch(() => {})
formRef.value
.validateField('goods_list.' + index + '.reduce_money')
.catch(() => {})
formRef.value.validateField('goods_list.' + index + '.specify_price').catch(() => {})
formRef.value.validateField('goods_list.' + index + '.reduce_money').catch(() => {})
}
}
} else if (discount_type == 'reduce') {
//
} else if (discount_type == 'reduce') {//
if (row.reduce_money.length) {
row.specify_price = (row.goodsSku.price - row.reduce_money).toFixed(2)
row.discount_price = (row.goodsSku.price - row.reduce_money).toFixed(2)
row.discount_rate = (
(row.specify_price / row.goodsSku.price) *
10
).toFixed(1)
row.discount_rate = (row.specify_price / row.goodsSku.price * 10).toFixed(1)
if (formRef.value) {
formRef.value
.validateField('goods_list.' + index + '.discount_rate')
.catch(() => {})
formRef.value
.validateField('goods_list.' + index + '.specify_price')
.catch(() => {})
formRef.value.validateField('goods_list.' + index + '.discount_rate').catch(() => {})
formRef.value.validateField('goods_list.' + index + '.specify_price').catch(() => {})
}
}
} else {
//
} else {//
if (row.specify_price.length) {
row.discount_price = row.specify_price + ''
row.reduce_money = (row.goodsSku.price - row.specify_price).toFixed(2)
row.discount_rate = (
(row.specify_price / row.goodsSku.price) *
10
).toFixed(1)
row.discount_rate = (row.specify_price / row.goodsSku.price * 10).toFixed(1)
if (formRef.value) {
formRef.value
.validateField('goods_list.' + index + '.discount_rate')
.catch(() => {})
formRef.value
.validateField('goods_list.' + index + '.reduce_money')
.catch(() => {})
formRef.value.validateField('goods_list.' + index + '.discount_rate').catch(() => {})
formRef.value.validateField('goods_list.' + index + '.reduce_money').catch(() => {})
}
}
}
row.discount_type = discount_type + ''
}
//sku
@ -1024,6 +736,7 @@ const enabledEvent = (row: any) => {
row.reduce_money = '0'
row.specify_price = row.goodsSku.price + ''
}
}
const back = () => {
router.push('/shop/marketing/discount/list')
@ -1041,7 +754,7 @@ input::-webkit-inner-spin-button {
margin: 0;
}
input[type='number'] {
input[type="number"] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
-o-appearance: textfield;

375
admin/src/addon/shop/views/marketing/discount/components/discount-detail.vue

@ -1,11 +1,5 @@
<template>
<el-drawer
v-model="showDialog"
title="活动详情"
direction="rtl"
:before-close="handleClose"
class="member-detail-drawer"
>
<el-drawer v-model="showDialog" title="活动详情" direction="rtl" :before-close="handleClose" class="member-detail-drawer">
<div class="main-container" v-loading="loading">
<el-tabs v-model="activeName" class="pb-[10px]" @tab-change="handleClick">
<el-tab-pane label="基础信息" name="basicInfo" />
@ -14,14 +8,7 @@
<el-tab-pane label="活动会员" name="memberList" />
</el-tabs>
<div v-if="activeName == 'basicInfo'">
<el-form
class="mt-[15px]"
:model="formData"
label-width="100px"
ref="formRef"
label-position="left"
v-if="Object.keys(formData).length"
>
<el-form class="mt-[15px]" :model="formData" label-width="100px" ref="formRef" label-position="left" v-if="Object.keys(formData).length">
<div class="relative" shadow="never" v-if="formData">
<el-row>
<el-col :span="8">
@ -94,67 +81,31 @@
<div v-if="activeName == 'goodsList'">
<el-form :inline="true" :model="goodsParams.searchParam">
<el-form-item :label="t('keyword')" prop="keyword">
<el-input
v-model.trim="goodsParams.searchParam.keyword"
:placeholder="t('keywordPlaceholder')"
/>
<el-input v-model.trim="goodsParams.searchParam.keyword" :placeholder="t('keywordPlaceholder')" />
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="getActiveDiscountGoodsPageListFn()"
>{{ t('search') }}</el-button
>
<el-button type="primary" @click="getActiveDiscountGoodsPageListFn()">{{ t('search') }}</el-button>
</el-form-item>
</el-form>
<el-table
:data="goodsParams.data"
size="large"
v-loading="goodsParams.loading"
>
<el-table :data="goodsParams.data" size="large" v-loading="goodsParams.loading">
<template #empty>
<span>{{ !goodsParams.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="goods_id"
:label="t('goodsInfo')"
min-width="300"
>
<el-table-column prop="goods_id" :label="t('goodsInfo')" min-width="300">
<template #default="{ row }">
<div
v-if="row.goods"
class="flex items-center cursor-pointer"
@click="previewEvent(row)"
>
<div
class="min-w-[70px] h-[70px] flex items-center justify-center"
>
<el-image
v-if="row.goods.goods_cover_thumb_small"
class="w-[70px] h-[70px]"
:src="img(row.goods.goods_cover_thumb_small)"
fit="contain"
>
<div v-if="row.goods" class="flex items-center cursor-pointer" @click="previewEvent(row)">
<div class="min-w-[70px] h-[70px] flex items-center justify-center">
<el-image v-if="row.goods.goods_cover_thumb_small" class="w-[70px] h-[70px]" :src="img(row.goods.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.goods.goods_name" class="multi-hidden">{{
row.goods.goods_name
}}</span>
<span :title="row.goods.goods_name" class="multi-hidden">{{ row.goods.goods_name }}</span>
</div>
</div>
</template>
@ -164,58 +115,26 @@
<span v-if="row.goodsSku">{{ row.goodsSku.price }}</span>
</template>
</el-table-column>
<el-table-column
prop="active_goods_order_money"
:label="t('paymentAmount')"
min-width="100"
/>
<el-table-column prop="active_goods_order_money" :label="t('paymentAmount')" min-width="100" />
<el-table-column
prop="active_goods_order_num"
:label="t('orderCount')"
min-width="100"
/>
<el-table-column
prop="active_goods_member_num"
:label="t('activeMemberNum')"
min-width="100"
/>
<el-table-column
prop="active_goods_success_num"
:label="t('activeSuccessNum')"
min-width="100"
/>
<el-table-column prop="active_goods_order_num" :label="t('orderCount')" min-width="100" />
<el-table-column prop="active_goods_member_num" :label="t('activeMemberNum')" min-width="100" />
<el-table-column prop="active_goods_success_num" :label="t('activeSuccessNum')" min-width="100" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="goodsParams.page"
v-model:page-size="goodsParams.limit"
:page-sizes="[6, 10, 20, 30, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="goodsParams.total"
@size-change="getActiveDiscountGoodsPageListFn()"
@current-change="getActiveDiscountGoodsPageListFn"
/>
<el-pagination v-model:current-page="goodsParams.page" v-model:page-size="goodsParams.limit" :page-sizes="[6,10,20,30,50,100]"
layout="total, sizes, prev, pager, next, jumper" :total="goodsParams.total"
@size-change="getActiveDiscountGoodsPageListFn()" @current-change="getActiveDiscountGoodsPageListFn" />
</div>
</div>
<div v-if="activeName == 'orderList'">
<el-form
:inline="true"
:model="orderParams.searchParam"
ref="orderSearchFormRef"
>
<el-form-item :label="t('orderInfo')" prop="search_name">
<el-input
class="input-item"
v-model.trim="orderParams.searchParam.search_name"
/>
<el-form :inline="true" :model="orderParams.searchParam" ref="orderSearchFormRef">
<el-form-item :label="t('orderInfo')" prop='search_name'>
<el-input class="input-item" v-model.trim="orderParams.searchParam.search_name" />
</el-form-item>
<el-form-item :label="t('payType')" prop="status">
<el-select
v-model="orderParams.searchParam.status"
clearable
class="input-item"
>
<el-form-item :label="t('payType')" prop='status'>
<el-select v-model="orderParams.searchParam.status" clearable class="input-item">
<el-option :label="t('toBePaid')" value="1"></el-option>
<el-option :label="t('toBeShipped')" value="2"></el-option>
<el-option :label="t('shipped')" value="3"></el-option>
@ -224,83 +143,43 @@
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker
v-model="orderParams.searchParam.create_time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
<el-date-picker v-model="orderParams.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item :label="t('payTime')" prop="pay_time">
<el-date-picker
v-model="orderParams.searchParam.pay_time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
<el-date-picker v-model="orderParams.searchParam.pay_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="getActiveDiscountOrderPageListFn()"
>{{ t('search') }}</el-button
>
<el-button @click="orderResetForm(orderSearchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="getActiveDiscountOrderPageListFn()">{{ t('search') }}</el-button>
<el-button @click="orderResetForm(orderSearchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-table
:data="orderParams.data"
size="large"
v-loading="orderParams.loading"
>
<el-table :data="orderParams.data" size="large" v-loading="orderParams.loading">
<template #empty>
<span>{{ !orderParams.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="order_no"
:label="t('orderNo')"
min-width="100"
>
<el-table-column prop="order_no" :label="t('orderNo')" min-width="100">
<template #default="{ row }">
<span
class="cursor-pointer text-primary"
@click="toGoodsCategoryEvent(row.order_id)"
>{{ row.order_no }}</span
>
<span class="cursor-pointer text-primary" @click="toGoodsCategoryEvent(row.order_id)">{{ row.order_no }}</span>
</template>
</el-table-column>
<el-table-column
prop="order_money"
:label="t('orderMoney')"
min-width="100"
/>
<el-table-column prop="order_money" :label="t('orderMoney')" min-width="100" />
<el-table-column :label="t('buyInfo')" min-width="120">
<template #default="{row}">
<div class="flex flex-col">
<span
class="text-[12px] text-primary cursor-pointer"
@click="detailEvent(row.member.member_id)"
>{{ row.member.nickname }}</span
>
<span class="text-[12px] mt-[5px]"
>{{ row.taker_name }} {{ row.taker_mobile }}</span
>
<span class="text-[12px] mt-[5px]">{{
row.taker_full_address
}}</span>
<span class="text-[12px] text-primary cursor-pointer" @click="detailEvent(row.member.member_id)">{{ row.member.nickname }}</span>
<span class="text-[12px] mt-[5px]">{{ row.taker_name }} {{row.taker_mobile }}</span>
<span class="text-[12px] mt-[5px]">{{ row.taker_full_address }}</span>
</div>
</template>
</el-table-column>
<el-table-column :label="t('payType')" min-width="120">
<template #default="{row}">
<span>{{
row.pay && row.pay.type_name ? row.pay.type_name : ''
}}</span>
<span>{{ row.pay && row.pay.type_name ? row.pay.type_name : ''}}</span>
</template>
</el-table-column>
<el-table-column :label="t('orderStatus')" min-width="100">
@ -308,107 +187,47 @@
<span class="text-[14px]">{{ row.order_status_data.name }}</span>
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="100"
/>
<el-table-column prop="create_time" :label="t('createTime')" min-width="100" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="orderParams.page"
v-model:page-size="orderParams.limit"
:page-sizes="[4, 10, 20, 30, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="orderParams.total"
@size-change="getActiveDiscountOrderPageListFn()"
@current-change="getActiveDiscountOrderPageListFn"
/>
<el-pagination v-model:current-page="orderParams.page" v-model:page-size="orderParams.limit" :page-sizes="[4,10,20,30,50,100]"
layout="total, sizes, prev, pager, next, jumper" :total="orderParams.total"
@size-change="getActiveDiscountOrderPageListFn()" @current-change="getActiveDiscountOrderPageListFn" />
</div>
</div>
<div v-if="activeName == 'memberList'">
<el-table
:data="memberParams.data"
size="large"
v-loading="memberParams.loading"
>
<el-table :data="memberParams.data" size="large" v-loading="memberParams.loading">
<template #empty>
<span>{{ !memberParams.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="goods_id"
:label="t('memberInfo')"
min-width="200"
>
<el-table-column prop="goods_id" :label="t('memberInfo')" min-width="200">
<template #default="{ row }">
<div
class="flex items-center cursor-pointer"
@click="detailEvent(row.member.member_id)"
>
<div
class="min-w-[50px] h-[50px] flex items-center justify-center"
>
<el-image
v-if="row.member.headimg"
class="w-[50px] h-[50px]"
:src="img(row.member.headimg)"
fit="contain"
>
<div class="flex items-center cursor-pointer" @click="detailEvent(row.member.member_id)">
<div class="min-w-[50px] h-[50px] flex items-center justify-center">
<el-image v-if="row.member.headimg" class="w-[50px] h-[50px]" :src="img(row.member.headimg)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[50px] h-[50px] rounded-full"
src="@/app/assets/images/member_head.png"
alt=""
/>
<img class="w-[50px] h-[50px] rounded-full" src="@/app/assets/images/member_head.png" alt="">
</div>
</template>
</el-image>
<img
class="w-[50px] h-[50px] rounded-full"
v-else
src="@/app/assets/images/member_head.png"
alt=""
/>
<img class="w-[50px] h-[50px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
</div>
<div class="ml-2">
<span
:title="row.member.nickname || row.member.username"
class="multi-hidden"
>{{ row.member.nickname || row.member.username }}</span
>
<span class="text-primary text-[12px]">{{
row.mobile || ''
}}</span>
<span :title="(row.member.nickname || row.member.username)" class="multi-hidden">{{row.member.nickname || row.member.username}}</span>
<span class="text-primary text-[12px]">{{row.mobile || ''}}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column
prop="active_order_money"
:label="t('consumptionMoney')"
min-width="100"
/>
<el-table-column
prop="member_count"
:label="t('participationNum')"
min-width="100"
/>
<el-table-column
prop="create_time"
:label="t('orderTime')"
min-width="100"
/>
<el-table-column prop="active_order_money" :label="t('consumptionMoney')" min-width="100" />
<el-table-column prop="member_count" :label="t('participationNum')" min-width="100" />
<el-table-column prop="create_time" :label="t('orderTime')" min-width="100" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="memberParams.page"
v-model:page-size="memberParams.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="memberParams.total"
@size-change="getActiveDiscountMemberPageList()"
@current-change="getActiveDiscountMemberPageList"
/>
<el-pagination v-model:current-page="memberParams.page" v-model:page-size="memberParams.limit"
layout="total, sizes, prev, pager, next, jumper" :total="memberParams.total"
@size-change="getActiveDiscountMemberPageList()" @current-change="getActiveDiscountMemberPageList" />
</div>
</div>
</div>
@ -418,12 +237,7 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getActiveDiscountInfo,
getActiveDiscountGoodsPageList,
getActiveDiscountOrderPageList,
getActiveDiscountMemberPageList,
} from '@/addon/shop/api/marketing'
import {getActiveDiscountInfo, getActiveDiscountGoodsPageList, getActiveDiscountOrderPageList, getActiveDiscountMemberPageList} from "@/addon/shop/api/marketing";
import { FormInstance } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
import { img } from '@/utils/common'
@ -442,8 +256,8 @@ const handleClick = (data: string) => {
}
const handleClose = (done: () => void) => {
activeName.value = 'basicInfo'
showDialog.value = false
activeName.value = 'basicInfo';
showDialog.value = false;
}
const getActiveDiscountInfoFn = (id:number)=>{
@ -463,8 +277,8 @@ const goodsParams = reactive({
data: [],
searchParam: {
keyword: '',
active_id: id,
},
active_id: id
}
})
const getActiveDiscountGoodsPageListFn = (page: number = 1)=>{
goodsParams.loading = true
@ -472,14 +286,12 @@ const getActiveDiscountGoodsPageListFn = (page: number = 1) => {
getActiveDiscountGoodsPageList({
page: goodsParams.page,
limit: goodsParams.limit,
...goodsParams.searchParam,
})
.then((res) => {
...goodsParams.searchParam
}).then(res=>{
goodsParams.loading = false
goodsParams.data = res.data.data
goodsParams.total = res.data.total
})
.catch(() => {
}).catch(() => {
goodsParams.loading = false
})
}
@ -488,8 +300,8 @@ const previewEvent = (data: any) => {
const url = router.resolve({
path: '/preview/wap',
query: {
page: `/addon/shop/pages/goods/detail?goods_id=${data.goods_id}`,
},
page: `/addon/shop/pages/goods/detail?goods_id=${data.goods_id}`
}
})
window.open(url.href)
}
@ -506,8 +318,8 @@ const orderParams = reactive({
status: '',
create_time: [],
pay_time: [],
active_id: '',
},
active_id: ''
}
})
const getActiveDiscountOrderPageListFn = (page: number = 1)=>{
@ -516,21 +328,19 @@ const getActiveDiscountOrderPageListFn = (page: number = 1) => {
getActiveDiscountOrderPageList({
page: orderParams.page,
limit: orderParams.limit,
...orderParams.searchParam,
})
.then((res) => {
...orderParams.searchParam
}).then(res => {
orderParams.loading = false
orderParams.data = res.data.data
orderParams.total = res.data.total
})
.catch(() => {
}).catch(() => {
orderParams.loading = false
})
}
const orderResetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
orderParams.searchParam.create_time = []
orderParams.searchParam.create_time = [];
getActiveDiscountOrderPageListFn()
}
//
@ -541,8 +351,8 @@ const memberParams = reactive({
loading: false,
data: [],
searchParam: {
active_id: id,
},
active_id: id
}
})
const getActiveDiscountMemberPageListFn= (page: number = 1)=>{
memberParams.loading = true
@ -550,14 +360,12 @@ const getActiveDiscountMemberPageListFn = (page: number = 1) => {
getActiveDiscountMemberPageList({
page: memberParams.page,
limit: memberParams.limit,
...memberParams.searchParam,
})
.then((res: any) => {
...memberParams.searchParam
}).then((res:any)=>{
memberParams.loading = false
memberParams.data = res.data.data
memberParams.total = res.data.total
})
.catch(() => {
}).catch(() => {
memberParams.loading = false
})
}
@ -565,15 +373,15 @@ const getActiveDiscountMemberPageListFn = (page: number = 1) => {
//
const detailEvent = (member_id:number)=> {
let routeData = router.resolve(`/member/detail?id=${member_id}`)
window.open(routeData.href, ' blank')
window.open(routeData.href, ' blank');
}
const setFormData = async (row: any = null) => {
id = row.id
id = row.id;
memberParams.searchParam.active_id = row.id
orderParams.searchParam.active_id = row.id
goodsParams.searchParam.active_id = row.id
memberParams.searchParam.active_id = row.id;
orderParams.searchParam.active_id = row.id;
goodsParams.searchParam.active_id = row.id;
getActiveDiscountMemberPageListFn()
getActiveDiscountOrderPageListFn()
@ -584,15 +392,16 @@ const setFormData = async (row: any = null) => {
const toGoodsCategoryEvent = (order_id:any) => {
// orderNo
const url = router.resolve({
path: '/shop/order/detail',
path: "/shop/order/detail",
query: { order_id: order_id }, // orderNo
})
window.open(url.href)
}
});
window.open(url.href);
};
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>
<style lang="scss">

362
admin/src/addon/shop/views/marketing/discount/components/goods-sku-popup.vue

@ -1,60 +1,27 @@
<template>
<el-drawer v-model="showDialog" :title="t('skuDiscountSettings')" size="55%">
<el-form
:model="formData"
label-width="120px"
ref="formRef"
class="page-form"
v-if="showDialog"
>
<el-form :model="formData" label-width="120px" ref="formRef" class="page-form" v-if="showDialog">
<div class="w-full sku_list">
<el-table
class="!w-[1400px] !max-w-[100%]"
v-if="goodsTable.list.length"
:data="goodsTable.list"
size="large"
ref="goods_listTableRef"
@selection-change="handleSelectionChange"
>
<el-table class="!w-[1400px] !max-w-[100%]" v-if="goodsTable.list.length" :data="goodsTable.list" size="large" ref="goods_listTableRef" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ t('emptyData')}}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
:label="t('goodsSelectPopupGoodsInfo')"
min-width="300"
>
<el-table-column :label="t('goodsSelectPopupGoodsInfo')" min-width="300">
<template #default="{ row }">
<div class="flex items-center cursor-pointer">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.sku_image"
class="w-[60px] h-[60px]"
:src="img(row.sku_image)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.sku_image" class="w-[60px] h-[60px]" :src="img(row.sku_image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.sku_name" class="multi-hidden">{{
row.sku_name || row.goods?.goods_name
}}</span>
<span :title="row.sku_name" class="multi-hidden">{{ row.sku_name||row.goods?.goods_name }}</span>
</div>
</div>
</template>
@ -63,12 +30,7 @@
<el-table-column :label="t('discounts')" width="170">
<template #default="{ row }">
<el-form-item
v-if="row.is_enabled"
:key="row.sku_id"
:prop="'skuList.' + row.index + '.discount_rate'"
:rules="[
{
<el-form-item v-if="row.is_enabled" :key="row.sku_id" :prop="'skuList.'+row.index + '.discount_rate'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
@ -80,32 +42,18 @@
} else if (value>9.9) {
callback(t('discountsTipsThree'))
} else {
callback()
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.discount_rate"
@blur="inputBlur(row, 'discount', row.index)"
clearable
placeholder="0.00"
maxlength="8"
/>
callback();
}
}
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.discount_rate" @blur="inputBlur(row,'discount',row.index)" clearable placeholder="0.00" maxlength="8" />
</el-form-item>
<span v-else>{{row.discount_rate}}</span>
</template>
</el-table-column>
<el-table-column :label="t('reduceMoney')" width="170">
<template #default="{ row }">
<el-form-item
v-if="row.is_enabled"
:key="row.sku_id"
:prop="'skuList.' + row.index + '.reduce_money'"
:rules="[
{
<el-form-item v-if="row.is_enabled" :key="row.sku_id" :prop="'skuList.'+row.index + '.reduce_money'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
@ -117,32 +65,18 @@
} else if (value >= parseFloat(row.price) ) {
callback(t('reduceMoneyTipsThree'))
} else {
callback()
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.reduce_money"
@blur="inputBlur(row, 'reduce', row.index)"
clearable
placeholder="0.00"
maxlength="8"
/>
callback();
}
}
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.reduce_money" @blur="inputBlur(row,'reduce',row.index)" clearable placeholder="0.00" maxlength="8" />
</el-form-item>
<span v-else>{{row.reduce_money}}</span>
</template>
</el-table-column>
<el-table-column :label="t('promotional')" width="170">
<template #default="{ row }">
<el-form-item
v-if="row.is_enabled"
:key="row.sku_id"
:prop="'skuList.' + row.index + '.specify_price'"
:rules="[
{
<el-form-item v-if="row.is_enabled" :key="row.sku_id" :prop="'skuList.'+row.index + '.specify_price'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
@ -154,118 +88,66 @@
}else if (value >= parseFloat(row.price) ) {
callback(t('promotionalTipsThree'))
} else {
callback()
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.specify_price"
clearable
@blur="inputBlur(row, 'specify', row.index)"
placeholder="0.00"
maxlength="8"
/>
callback();
}
}
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.specify_price" clearable @blur="inputBlur(row,'specify',row.index)" placeholder="0.00" maxlength="8" />
</el-form-item>
<span v-else>{{row.specify_price}}</span>
</template>
</el-table-column>
<el-table-column :label="t('discountType')" width="130">
<template #default="{ row }">
<span>{{
row.discount_type == 'discount'
? t('discounts')
: row.discount_type == 'reduce'
? t('reduceMoney')
: t('promotional')
}}</span>
<span>{{row.discount_type=='discount'?t('discounts'):row.discount_type=='reduce'?t('reduceMoney'):t('promotional')}}</span>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="160"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="160">
<template #default="{row}">
<el-button type="primary" link @click="enabledEvent(row)">{{
row.is_enabled ? t('noEnabled') : t('enabled')
}}</el-button>
<el-button type="primary" link @click="enabledEvent(row)">{{ row.is_enabled?t('noEnabled'):t('enabled') }}</el-button>
</template>
</el-table-column>
</el-table>
<div
class="flex items-center justify-between mt-[15px] !w-[1400px] !max-w-[100%]"
>
<div class="flex items-center justify-between mt-[15px] !w-[1400px] !max-w-[100%]">
<div class="flex items-center mb-[15px]">
<el-checkbox
v-model="toggleCheckbox"
size="large"
class="!mr-[15px]"
@change="toggleChange"
:indeterminate="isIndeterminate"
>
<el-checkbox v-model="toggleCheckbox" size="large" class="!mr-[15px]" @change="toggleChange" :indeterminate="isIndeterminate">
<span>已选{{ multipleSelection.length }}</span>
</el-checkbox>
<label>{{ t('batchOperation') }}</label>
<el-select
v-model="batchOperation.discount_type"
class="!w-[130px] ml-[10px]"
@change="batchOperation.discountNumber = ''"
>
<el-select v-model="batchOperation.discount_type" class="!w-[130px] ml-[10px]" @change="batchOperation.discountNumber=''">
<el-option :label="t('discounts')" value="discount" />
<el-option :label="t('reduceMoney')" value="reduce" />
<el-option :label="t('promotional')" value="specify" />
</el-select>
<el-input
v-model.trim="batchOperation.discountNumber"
clearable
:placeholder="
batchOperation.discount_type == 'discount'
? t('discounts')
: batchOperation.discount_type == 'reduce'
? t('reduceMoney')
: t('promotional')
"
class="!w-[130px] ml-[10px]"
maxlength="8"
/>
<el-button class="ml-[10px]" type="primary" @click="saveBatch">{{
t('confirm')
}}</el-button>
<el-input v-model.trim="batchOperation.discountNumber" clearable
:placeholder="batchOperation.discount_type=='discount'?t('discounts'):batchOperation.discount_type=='reduce'?t('reduceMoney'):t('promotional')"
class="!w-[130px] ml-[10px]" maxlength="8" />
<el-button class="ml-[10px]" type="primary" @click="saveBatch">{{ t('confirm') }}</el-button>
</div>
<el-pagination
v-model:current-page="goodsTable.page"
v-model:page-size="goodsTable.limit"
layout="total, prev, pager, next, jumper"
:total="goodsTable.total"
@current-change="setGoodsList"
/>
<el-pagination v-model:current-page="goodsTable.page" v-model:page-size="goodsTable.limit"
layout="total, prev, pager, next, jumper" :total="goodsTable.total"
@current-change="setGoodsList" />
</div>
</div>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="save(formRef)">{{
t('confirm')
}}</el-button>
<el-button @click="showDialog = false">{{ t("cancel") }}</el-button>
<el-button type="primary" @click="save(formRef)">{{ t("confirm") }}</el-button>
</span>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import { img, deepClone } from '@/utils/common'
import {t} from "@/lang";
import {img, deepClone} from "@/utils/common";
import {FormInstance, ElMessage} from 'element-plus'
import { ref, reactive, nextTick } from 'vue'
import {ref, reactive, nextTick} from "vue";
const showDialog = ref(false)
const emit = defineEmits(['skuSave'])
const showDialog = ref(false);
const emit = defineEmits(["skuSave"]);
const formData: Record<string, any> = ref({skuList: []})
const formRef = ref<FormInstance>()
@ -273,7 +155,7 @@ const formRef = ref<FormInstance>()
//
const regExp = {
number: /^\d{0,10}(.?\d{0,1})$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
digit: /^\d{0,10}(.?\d{0,2})$/
}
const show = (data: any) => {
@ -283,16 +165,16 @@ const show = (data: any) => {
el.index = index
})
setGoodsList()
showDialog.value = true
}
showDialog.value = true;
};
//
interface goodsTableInterface {
page: number
limit: number
total: number
data: any
list: Array<any>
page: number,
limit: number,
total: number,
data: any,
list: Array<any>,
}
const goodsTable = reactive<goodsTableInterface>({
@ -300,7 +182,7 @@ const goodsTable = reactive<goodsTableInterface>({
limit: 5,
total: 0,
data: [],
list: [],
list: []
})
const setGoodsList = (page = 1) => {
@ -325,22 +207,22 @@ const setGoodsList = (page = 1) => {
//
const splitArray = (array: [], size: number) => {
var result = []
var result = [];
for (var i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size))
result.push(array.slice(i, i + size));
}
return result
return result;
}
/*****批量设置 ****/
interface batchOperationInterface {
discount_type: any
discountNumber: any
discount_type: any,
discountNumber: any,
}
const batchOperation = ref<batchOperationInterface>({
discount_type: 'discount',
discountNumber: '',
discountNumber: ''
})
//
@ -364,10 +246,7 @@ const handleSelectionChange = (val: []) => {
multipleSelection.value = val
toggleCheckbox.value = false
if (
multipleSelection.value.length > 0 &&
multipleSelection.value.length < goodsTable.list.length
) {
if (multipleSelection.value.length > 0 && multipleSelection.value.length < goodsTable.list.length) {
isIndeterminate.value = true
} else {
isIndeterminate.value = false
@ -381,7 +260,7 @@ const saveBatch = () => {
if (!multipleSelection.value.length) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`,
message: `${t('batchEmptySelectedGoodsTips')}`
})
return
}
@ -389,28 +268,25 @@ const saveBatch = () => {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('discountsPlaceholder')}`,
message: `${t('discountsPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.number.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.number.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('discountsTips')}`,
message: `${t('discountsTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('discountsTipsTwo')}`,
message: `${t('discountsTipsTwo')}`
})
return
} else if (batchOperation.value.discountNumber > 9.9) {
ElMessage({
type: 'warning',
message: `${t('discountsTipsThree')}`,
message: `${t('discountsTipsThree')}`
})
return
}
@ -418,78 +294,68 @@ const saveBatch = () => {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyPlaceholder')}`,
message: `${t('reduceMoneyPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.digit.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.digit.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyTips')}`,
message: `${t('reduceMoneyTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyTipsTwo')}`,
message: `${t('reduceMoneyTipsTwo')}`
})
return
}
} else {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('promotionalPlaceholder')}`,
message: `${t('promotionalPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.digit.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.digit.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('promotionalTips')}`,
message: `${t('promotionalTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('promotionalTipsTwo')}`,
message: `${t('promotionalTipsTwo')}`
})
return
}
}
formData.value.skuList.forEach((el: any, index: number) => {
multipleSelection.value.forEach((v: any) => {
if (v.sku_id === el.sku_id&&el.is_enabled===1) {
if (batchOperation.value.discount_type == 'discount') {
//
el.discount_rate = batchOperation.value.discountNumber + ''
//
el.specify_price = (
el.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
el.discount_price = (
el.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
el.specify_price = (el.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
el.discount_price = (el.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
//
el.reduce_money = (el.price - el.specify_price).toFixed(2)
} else if (batchOperation.value.discount_type == 'reduce') {
//
} else if (batchOperation.value.discount_type == 'reduce') {//
el.reduce_money = batchOperation.value.discountNumber + ''
el.specify_price = (el.price - el.reduce_money).toFixed(2)
el.discount_price = (el.price - el.reduce_money).toFixed(2)
el.discount_rate = ((el.specify_price / el.price) * 10).toFixed(1)
} else {
//
el.discount_rate = (el.specify_price / el.price * 10).toFixed(1)
} else {//
el.specify_price = batchOperation.value.discountNumber + ''
el.discount_price = batchOperation.value.discountNumber + ''
el.reduce_money = (el.price - el.specify_price).toFixed(2)
el.discount_rate = ((el.specify_price / el.price) * 10).toFixed(1)
el.discount_rate = (el.specify_price / el.price * 10).toFixed(1)
}
el.discount_type = batchOperation.value.discount_type + ''
if (formRef.value) {
@ -516,20 +382,20 @@ const inputBlur = (row: any, discount_type: string, index: number) => {
//
row.reduce_money = (row.price - row.specify_price).toFixed(2)
}
} else if (discount_type == 'reduce') {
//
} else if (discount_type == 'reduce') { //
if (row.reduce_money.length) {
row.specify_price = (row.price - row.reduce_money).toFixed(2)
row.discount_price = (row.price - row.reduce_money).toFixed(2)
row.discount_rate = ((row.specify_price / row.price) * 10).toFixed(1)
row.discount_rate = (row.specify_price / row.price * 10).toFixed(1)
}
} else {
//
} else { //
if (row.specify_price.length) {
row.discount_price = row.specify_price + ''
row.reduce_money = (row.price - row.specify_price).toFixed(2)
row.discount_rate = ((row.specify_price / row.price) * 10).toFixed(1)
row.discount_rate = (row.specify_price / row.price * 10).toFixed(1)
}
}
row.discount_type = discount_type + ''
if (formRef.value) {
@ -554,6 +420,7 @@ const enabledEvent = (row: any) => {
row.reduce_money = '0'
row.specify_price = row.price + ''
}
}
const validFn = (row: any) => {
if(row.is_enabled===0){
@ -561,10 +428,7 @@ const validFn = (row: any) => {
}
if (row.discount_rate.length == 0) {
return false
} else if (
isNaN(row.discount_rate) ||
!regExp.number.test(row.discount_rate)
) {
} else if (isNaN(row.discount_rate) || !regExp.number.test(row.discount_rate)) {
return false
} else if (row.discount_rate <= 0) {
return false
@ -580,10 +444,7 @@ const validFn = (row: any) => {
return false
} else if (row.specify_price.length == 0) {
return false
} else if (
isNaN(row.specify_price) ||
!regExp.digit.test(row.specify_price)
) {
} else if (isNaN(row.specify_price) || !regExp.digit.test(row.specify_price)) {
return false
} else if (row.specify_price <= 0) {
return false
@ -598,42 +459,36 @@ const save = (formEl: FormInstance | undefined) => {
if (!formEl) return
for (var i = 0; i < formData.value.skuList.length; i++) {
if (!validFn(formData.value.skuList[i])) {
let page = Math.ceil(
i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit
)
let page = Math.ceil(i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit)
goodsTable.list = goodsTable.data[page - 1]
goodsTable.page = page
break
break;
}
}
nextTick(async () => {
await formEl.validate((valid) => {
if (valid) {
formData.value.valid = true
let discount_rate_list = formData.value.skuList
.filter((el: any) => el.is_enabled === 1)
.map((el: any) => Number(el.discount_rate))
let reduce_money_list = formData.value.skuList
.filter((el: any) => el.is_enabled === 1)
.map((el: any) => Number(el.reduce_money))
let specify_price_list = formData.value.skuList
.filter((el: any) => el.is_enabled === 1)
.map((el: any) => Number(el.specify_price))
let discount_rate_list = formData.value.skuList.filter((el:any)=>el.is_enabled===1).map((el:any)=>Number(el.discount_rate))
let reduce_money_list = formData.value.skuList.filter((el:any)=>el.is_enabled===1).map((el:any)=>Number(el.reduce_money))
let specify_price_list = formData.value.skuList.filter((el:any)=>el.is_enabled===1).map((el:any)=>Number(el.specify_price))
formData.value.max_discount_rate = Math.max(...discount_rate_list)
formData.value.min_discount_rate = Math.min(...discount_rate_list)
formData.value.max_reduce_money = Math.max(...reduce_money_list)
formData.value.min_reduce_money = Math.min(...reduce_money_list)
formData.value.max_specify_price = Math.max(...specify_price_list)
formData.value.min_specify_price = Math.min(...specify_price_list)
emit('skuSave', formData.value)
showDialog.value = false
emit("skuSave", formData.value);
showDialog.value = false;
}
})
})
}
};
defineExpose({
show,
})
show
});
</script>
<style lang="scss" scoped>
@ -647,7 +502,7 @@ input::-webkit-inner-spin-button {
margin: 0;
}
input[type='number'] {
input[type="number"] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
-o-appearance: textfield;
@ -664,3 +519,4 @@ input[type='number'] {
overflow: initial !important;
}
</style>

98
admin/src/addon/shop/views/marketing/discount/config.vue

@ -1,31 +1,20 @@
<template>
<div class="main-container" v-loading="loading">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
</div>
<div class="flex mt-[20px]">
<div
class="relative overflow-hidden w-[340px] h-[680px] z-0 bg-[#f5f5f5]"
>
<img
class="absolute top-0 left-0 z-10 pointer-events-none"
src="@/addon/shop/assets/discount_config.png"
/>
<div class="relative overflow-hidden w-[340px] h-[680px] z-0 bg-[#f5f5f5]">
<img class="absolute top-0 left-0 z-10 pointer-events-none" src="@/addon/shop/assets/discount_config.png"/>
<div class="absolute top-0 left-0 w-[340px]">
<img
class="w-full h-[256px]"
v-if="isShow"
src="@/addon/shop/assets/discount_banner.png"
/>
<img class="w-full h-[256px]" v-if="isShow" src="@/addon/shop/assets/discount_banner.png">
<el-carousel height="256px" arrow="never" v-else>
<template
v-for="(item, index) in formData.list"
:key="'img' + index"
>
<template v-for="(item,index) in formData.list" :key="'img'+index">
<el-carousel-item v-if="item.imageUrl">
<img class="w-full h-full" :src="img(item.imageUrl)" />
<img class="w-full h-full" :src="img(item.imageUrl)">
</el-carousel-item>
</template>
</el-carousel>
@ -34,22 +23,9 @@
<div class="ml-[20px]">
<h3 class="panel-title !text-sm">{{t('headTitle')}}</h3>
<el-form
class="page-form"
:model="formData"
label-width="120px"
ref="formRef"
>
<div
v-for="(item, index) in formData.list"
class="border-[1px] border-[var(--el-border-color)] border-dashed w-[500px] pt-[15px] mb-[15px] relative item"
:key="index"
>
<el-form-item
:label="t('image')"
:prop="`list.${index}.imageUrl`"
:rules="[
{
<el-form class="page-form" :model="formData" label-width="120px" ref="formRef">
<div v-for="(item,index) in formData.list" class="border-[1px] border-[var(--el-border-color)] border-dashed w-[500px] pt-[15px] mb-[15px] relative item" :key="index">
<el-form-item :label="t('image')" :prop="`list.${index}.imageUrl`" :rules="[{
required: true,
trigger: 'change',
validator: (rule: any, value: any, callback: any) => {
@ -57,17 +33,11 @@
callback(t('imagePlaceholder'))
}
callback()
},
},
]"
>
}
}]">
<upload-image v-model="item.imageUrl" :limit="1"/>
</el-form-item>
<el-form-item
:label="t('toLink')"
:prop="`list.${index}.toLink.name`"
:rules="[
{
<el-form-item :label="t('toLink')" :prop="`list.${index}.toLink.name`" :rules="[{
required: true,
trigger: 'change',
validator: (rule: any, value: any, callback: any) => {
@ -75,24 +45,14 @@
callback(t('toLinkPlaceholder'))
}
callback()
},
},
]"
>
}
}]">
<diy-link v-model="item.toLink"/>
</el-form-item>
<span
v-if="formData.list.length > 1"
class="cursor-pointer absolute top-[-8px] right-[-8px] delete"
@click="deleteConfigList(index)"
><el-icon color="#bbbbbb" size="20px"
><CircleCloseFilled /></el-icon
></span>
<span v-if="formData.list.length>1" class="cursor-pointer absolute top-[-8px] right-[-8px] delete" @click="deleteConfigList(index)"><el-icon color="#bbbbbb" size="20px"><CircleCloseFilled /></el-icon></span>
</div>
<div class="flex w-full justify-center">
<el-button class="w-[400px]" @click="addConfigList">{{
t('addConfigList')
}}</el-button>
<el-button class="w-[400px]" @click="addConfigList">{{ t('addConfigList') }}</el-button>
</div>
</el-form>
</div>
@ -102,9 +62,7 @@
<!-- 提交按钮 -->
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
</div>
</div>
</div>
@ -114,10 +72,7 @@
import { ref,computed } from 'vue'
import { t } from '@/lang'
import { img } from '@/utils/common'
import {
getActiveDiscountConfig,
editActiveDiscountConfig,
} from '@/addon/shop/api/marketing'
import {getActiveDiscountConfig,editActiveDiscountConfig} from "@/addon/shop/api/marketing";
import { FormInstance } from 'element-plus'
import { useRoute } from 'vue-router'
@ -129,20 +84,21 @@ const isShow = computed(() => {
})
const formData: Record<string, any> = ref({
list: [{ imageUrl: '', toLink: { name: '' } }],
list: [
{imageUrl: '', toLink: {name: ''}},
]
})
const formRef = ref<FormInstance>()
const getActiveDiscountConfigFn=()=>{
loading.value = true
getActiveDiscountConfig()
.then((res: any) => {
getActiveDiscountConfig().then((res:any)=>{
if(res.data.length) formData.value.list=res.data
loading.value = false
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
getActiveDiscountConfigFn()
const addConfigList = ()=>{
@ -156,12 +112,10 @@ const onSave = async (formEl: FormInstance | undefined) => {
await formEl.validate(async (valid) => {
if (valid) {
loading.value = true
editActiveDiscountConfig(formData.value)
.then((res: any) => {
editActiveDiscountConfig(formData.value).then((res:any) => {
loading.value = false
getActiveDiscountConfigFn()
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}

348
admin/src/addon/shop/views/marketing/discount/detail.vue

@ -1,22 +1,12 @@
<template>
<div class="main-container" v-loading="loading">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-form
class="page-form mt-[15px]"
:model="formData"
label-width="100px"
ref="formRef"
label-position="left"
v-if="Object.keys(formData).length"
>
<el-card
class="box-card !border-none relative"
shadow="never"
v-if="formData"
>
<el-form class="page-form mt-[15px]" :model="formData" label-width="100px" ref="formRef" label-position="left" v-if="Object.keys(formData).length">
<el-card class="box-card !border-none relative" shadow="never" v-if="formData">
<h3 class="panel-title">{{ t('baseInfo') }}</h3>
<div class="px-[30px] mb-[20px]">
<el-row>
@ -87,11 +77,7 @@
</div>
</el-card>
</el-form>
<el-card
class="box-card !border-none relative"
shadow="never"
v-if="Object.keys(formData).length"
>
<el-card class="box-card !border-none relative" shadow="never" v-if="Object.keys(formData).length">
<el-tabs v-model="activeName" class="py-[10px]" @tab-change="handleClick">
<el-tab-pane label="活动商品" name="goodsList" />
<el-tab-pane label="活动订单" name="orderList" />
@ -100,68 +86,31 @@
<div v-if="activeName == 'goodsList'">
<el-form :inline="true" :model="goodsParams.searchParam">
<el-form-item :label="t('keyword')" prop="keyword">
<el-input
v-model.trim="goodsParams.searchParam.keyword"
clearable
:placeholder="t('keywordPlaceholder')"
/>
<el-input v-model.trim="goodsParams.searchParam.keyword" clearable :placeholder="t('keywordPlaceholder')" />
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="getActiveDiscountGoodsPageListFn()"
>{{ t('search') }}</el-button
>
<el-button type="primary" @click="getActiveDiscountGoodsPageListFn()">{{ t('search') }}</el-button>
</el-form-item>
</el-form>
<el-table
:data="goodsParams.data"
size="large"
v-loading="goodsParams.loading"
>
<el-table :data="goodsParams.data" size="large" v-loading="goodsParams.loading">
<template #empty>
<span>{{ !goodsParams.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="goods_id"
:label="t('goodsInfo')"
min-width="300"
>
<el-table-column prop="goods_id" :label="t('goodsInfo')" min-width="300">
<template #default="{ row }">
<div
v-if="row.goods"
class="flex items-center cursor-pointer"
@click="previewEvent(row)"
>
<div
class="min-w-[70px] h-[70px] flex items-center justify-center"
>
<el-image
v-if="row.goods.goods_cover_thumb_small"
class="w-[70px] h-[70px]"
:src="img(row.goods.goods_cover_thumb_small)"
fit="contain"
>
<div v-if="row.goods" class="flex items-center cursor-pointer" @click="previewEvent(row)">
<div class="min-w-[70px] h-[70px] flex items-center justify-center">
<el-image v-if="row.goods.goods_cover_thumb_small" class="w-[70px] h-[70px]" :src="img(row.goods.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[70px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[70px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.goods.goods_name" class="multi-hidden">{{
row.goods.goods_name
}}</span>
<span :title="row.goods.goods_name" class="multi-hidden">{{ row.goods.goods_name }}</span>
</div>
</div>
</template>
@ -171,57 +120,26 @@
<span v-if="row.goodsSku">{{ row.goodsSku.price }}</span>
</template>
</el-table-column>
<el-table-column
prop="active_goods_order_money"
:label="t('paymentAmount')"
min-width="100"
/>
<el-table-column prop="active_goods_order_money" :label="t('paymentAmount')" min-width="100" />
<el-table-column
prop="active_goods_order_num"
:label="t('orderCount')"
min-width="100"
/>
<el-table-column
prop="active_goods_member_num"
:label="t('activeMemberNum')"
min-width="100"
/>
<el-table-column
prop="active_goods_success_num"
:label="t('activeSuccessNum')"
min-width="100"
/>
<el-table-column prop="active_goods_order_num" :label="t('orderCount')" min-width="100" />
<el-table-column prop="active_goods_member_num" :label="t('activeMemberNum')" min-width="100" />
<el-table-column prop="active_goods_success_num" :label="t('activeSuccessNum')" min-width="100" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="goodsParams.page"
v-model:page-size="goodsParams.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="goodsParams.total"
@size-change="getActiveDiscountGoodsPageListFn()"
@current-change="getActiveDiscountGoodsPageListFn"
/>
<el-pagination v-model:current-page="goodsParams.page" v-model:page-size="goodsParams.limit"
layout="total, sizes, prev, pager, next, jumper" :total="goodsParams.total"
@size-change="getActiveDiscountGoodsPageListFn()" @current-change="getActiveDiscountGoodsPageListFn" />
</div>
</div>
<div v-if="activeName == 'orderList'">
<el-form
:inline="true"
:model="orderParams.searchParam"
ref="orderSearchFormRef"
>
<el-form-item :label="t('orderInfo')" prop="search_name">
<el-input
class="input-item"
v-model.trim="orderParams.searchParam.search_name"
/>
<el-form :inline="true" :model="orderParams.searchParam" ref="orderSearchFormRef">
<el-form-item :label="t('orderInfo')" prop='search_name'>
<el-input class="input-item" v-model.trim="orderParams.searchParam.search_name" />
</el-form-item>
<el-form-item :label="t('payType')" prop="status">
<el-select
v-model="orderParams.searchParam.status"
clearable
class="input-item"
>
<el-form-item :label="t('payType')" prop='status'>
<el-select v-model="orderParams.searchParam.status" clearable class="input-item">
<el-option :label="t('toBePaid')" value="1"></el-option>
<el-option :label="t('toBeShipped')" value="2"></el-option>
<el-option :label="t('shipped')" value="3"></el-option>
@ -230,74 +148,38 @@
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker
v-model="orderParams.searchParam.create_time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
<el-date-picker v-model="orderParams.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item :label="t('payTime')" prop="pay_time">
<el-date-picker
v-model="orderParams.searchParam.pay_time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
<el-date-picker v-model="orderParams.searchParam.pay_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="getActiveDiscountOrderPageListFn()"
>{{ t('search') }}</el-button
>
<el-button @click="orderResetForm(orderSearchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="getActiveDiscountOrderPageListFn()">{{ t('search') }}</el-button>
<el-button @click="orderResetForm(orderSearchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-table
:data="orderParams.data"
size="large"
v-loading="orderParams.loading"
>
<el-table :data="orderParams.data" size="large" v-loading="orderParams.loading">
<template #empty>
<span>{{ !orderParams.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="order_no"
:label="t('orderNo')"
min-width="100"
/>
<el-table-column
prop="order_money"
:label="t('orderMoney')"
min-width="100"
/>
<el-table-column prop="order_no" :label="t('orderNo')" min-width="100" />
<el-table-column prop="order_money" :label="t('orderMoney')" min-width="100" />
<el-table-column :label="t('buyInfo')" min-width="120">
<template #default="{row}">
<div class="flex flex-col">
<span
class="text-[12px] text-primary cursor-pointer"
@click="detailEvent(row.member.member_id)"
>{{ row.member.nickname }}</span
>
<span class="text-[12px] mt-[5px]"
>{{ row.taker_name }} {{ row.taker_mobile }}</span
>
<span class="text-[12px] mt-[5px]">{{
row.taker_full_address
}}</span>
<span class="text-[12px] text-primary cursor-pointer" @click="detailEvent(row.member.member_id)">{{ row.member.nickname }}</span>
<span class="text-[12px] mt-[5px]">{{ row.taker_name }} {{row.taker_mobile }}</span>
<span class="text-[12px] mt-[5px]">{{ row.taker_full_address }}</span>
</div>
</template>
</el-table-column>
<el-table-column :label="t('payType')" min-width="120">
<template #default="{row}">
<span>{{
row.pay && row.pay.type_name ? row.pay.type_name : ''
}}</span>
<span>{{ row.pay && row.pay.type_name ? row.pay.type_name : ''}}</span>
</template>
</el-table-column>
<el-table-column :label="t('orderStatus')" min-width="100">
@ -305,106 +187,47 @@
<span class="text-[14px]">{{ row.order_status_data.name }}</span>
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="100"
/>
<el-table-column prop="create_time" :label="t('createTime')" min-width="100" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="orderParams.page"
v-model:page-size="orderParams.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="orderParams.total"
@size-change="getActiveDiscountOrderPageListFn()"
@current-change="getActiveDiscountOrderPageListFn"
/>
<el-pagination v-model:current-page="orderParams.page" v-model:page-size="orderParams.limit"
layout="total, sizes, prev, pager, next, jumper" :total="orderParams.total"
@size-change="getActiveDiscountOrderPageListFn()" @current-change="getActiveDiscountOrderPageListFn" />
</div>
</div>
<div v-if="activeName == 'memberList'">
<el-table
:data="memberParams.data"
size="large"
v-loading="memberParams.loading"
>
<el-table :data="memberParams.data" size="large" v-loading="memberParams.loading">
<template #empty>
<span>{{ !memberParams.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="goods_id"
:label="t('memberInfo')"
min-width="200"
>
<el-table-column prop="goods_id" :label="t('memberInfo')" min-width="200">
<template #default="{ row }">
<div
class="flex items-center cursor-pointer"
@click="detailEvent(row.member.member_id)"
>
<div
class="min-w-[50px] h-[50px] flex items-center justify-center"
>
<el-image
v-if="row.member.headimg"
class="w-[50px] h-[50px]"
:src="img(row.member.headimg)"
fit="contain"
>
<div class="flex items-center cursor-pointer" @click="detailEvent(row.member.member_id)">
<div class="min-w-[50px] h-[50px] flex items-center justify-center">
<el-image v-if="row.member.headimg" class="w-[50px] h-[50px]" :src="img(row.member.headimg)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[50px] h-[50px] rounded-full"
src="@/app/assets/images/member_head.png"
alt=""
/>
<img class="w-[50px] h-[50px] rounded-full" src="@/app/assets/images/member_head.png" alt="">
</div>
</template>
</el-image>
<img
class="w-[50px] h-[50px] rounded-full"
v-else
src="@/app/assets/images/member_head.png"
alt=""
/>
<img class="w-[50px] h-[50px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
</div>
<div class="ml-2">
<span
:title="row.member.nickname || row.member.username"
class="multi-hidden"
>{{ row.member.nickname || row.member.username }}</span
>
<span class="text-primary text-[12px]">{{
row.mobile || ''
}}</span>
<span :title="(row.member.nickname || row.member.username)" class="multi-hidden">{{row.member.nickname || row.member.username}}</span>
<span class="text-primary text-[12px]">{{row.mobile || ''}}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column
prop="active_order_money"
:label="t('consumptionMoney')"
min-width="100"
/>
<el-table-column
prop="member_count"
:label="t('participationNum')"
min-width="100"
/>
<el-table-column
prop="create_time"
:label="t('orderTime')"
min-width="100"
/>
<el-table-column prop="active_order_money" :label="t('consumptionMoney')" min-width="100" />
<el-table-column prop="member_count" :label="t('participationNum')" min-width="100" />
<el-table-column prop="create_time" :label="t('orderTime')" min-width="100" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="memberParams.page"
v-model:page-size="memberParams.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="memberParams.total"
@size-change="getActiveDiscountMemberPageList()"
@current-change="getActiveDiscountMemberPageList"
/>
<el-pagination v-model:current-page="memberParams.page" v-model:page-size="memberParams.limit"
layout="total, sizes, prev, pager, next, jumper" :total="memberParams.total"
@size-change="getActiveDiscountMemberPageList()" @current-change="getActiveDiscountMemberPageList" />
</div>
</div>
</el-card>
@ -417,12 +240,7 @@ import { t } from '@/lang'
import { img } from '@/utils/common'
import {FormInstance } from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import {
getActiveDiscountInfo,
getActiveDiscountGoodsPageList,
getActiveDiscountOrderPageList,
getActiveDiscountMemberPageList,
} from '@/addon/shop/api/marketing'
import {getActiveDiscountInfo, getActiveDiscountGoodsPageList, getActiveDiscountOrderPageList, getActiveDiscountMemberPageList} from "@/addon/shop/api/marketing";
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
@ -460,8 +278,8 @@ const goodsParams = reactive({
data: [],
searchParam: {
keyword: '',
active_id: route.query.id,
},
active_id: route.query.id
}
})
const getActiveDiscountGoodsPageListFn = (page: number = 1)=>{
goodsParams.loading = true
@ -469,14 +287,12 @@ const getActiveDiscountGoodsPageListFn = (page: number = 1) => {
getActiveDiscountGoodsPageList({
page: goodsParams.page,
limit: goodsParams.limit,
...goodsParams.searchParam,
})
.then((res) => {
...goodsParams.searchParam
}).then(res=>{
goodsParams.loading = false
goodsParams.data = res.data.data
goodsParams.total = res.data.total
})
.catch(() => {
}).catch(() => {
goodsParams.loading = false
})
}
@ -486,8 +302,8 @@ const previewEvent = (data: any) => {
const url = router.resolve({
path: '/preview/wap',
query: {
page: `/addon/shop/pages/goods/detail?goods_id=${data.goods_id}`,
},
page: `/addon/shop/pages/goods/detail?goods_id=${data.goods_id}`
}
})
window.open(url.href)
}
@ -504,8 +320,8 @@ const orderParams = reactive({
status: '',
create_time: [],
pay_time: [],
active_id: route.query.id,
},
active_id: route.query.id
}
})
const getActiveDiscountOrderPageListFn = (page: number = 1)=>{
orderParams.loading = true
@ -513,14 +329,12 @@ const getActiveDiscountOrderPageListFn = (page: number = 1) => {
getActiveDiscountOrderPageList({
page: orderParams.page,
limit: orderParams.limit,
...orderParams.searchParam,
})
.then((res) => {
...orderParams.searchParam
}).then(res => {
orderParams.loading = false
orderParams.data = res.data.data
orderParams.total = res.data.total
})
.catch(() => {
}).catch(() => {
orderParams.loading = false
})
}
@ -538,8 +352,8 @@ const memberParams = reactive({
loading: false,
data: [],
searchParam: {
active_id: route.query.id,
},
active_id:route.query.id
}
})
const getActiveDiscountMemberPageListFn= (page: number = 1)=>{
memberParams.loading = true
@ -547,14 +361,12 @@ const getActiveDiscountMemberPageListFn = (page: number = 1) => {
getActiveDiscountMemberPageList({
page: memberParams.page,
limit: memberParams.limit,
...memberParams.searchParam,
})
.then((res: any) => {
...memberParams.searchParam
}).then((res:any)=>{
memberParams.loading = false
memberParams.data = res.data.data
memberParams.total = res.data.total
})
.catch(() => {
}).catch(() => {
memberParams.loading = false
})
}
@ -563,7 +375,7 @@ getActiveDiscountMemberPageListFn()
//
const detailEvent = (member_id:number)=> {
let routeData = router.resolve(`/member/detail?id=${member_id}`)
window.open(routeData.href, ' blank')
window.open(routeData.href, ' blank');
}
</script>

572
admin/src/addon/shop/views/marketing/discount/edit.vue

@ -1,124 +1,64 @@
<template>
<div class="main-container">
<el-card class="card !border-none" shadow="never">
<el-page-header :content="pageName" :icon="ArrowLeft" @back="back()" />
</el-card>
<!-- 表单 -->
<el-card
class="box-card mt-[15px] !border-none"
shadow="never"
v-loading="loading"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
>
<el-card class="box-card mt-[15px] !border-none" shadow="never" v-loading="loading">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form">
<!-- 活动名称 -->
<el-form-item :label="t('name')" prop="active_name">
<div>
<el-input
v-model.trim="formData.active_name"
clearable
:placeholder="t('namePlaceholder')"
class="input-width"
:maxlength="20"
/>
<el-input v-model.trim="formData.active_name" clearable :placeholder="t('namePlaceholder')" class="input-width" :maxlength="20" />
<p class=" text-[14px] text-[#999]">{{ t('nameTip') }}</p>
</div>
</el-form-item>
<!-- 活动标题 -->
<el-form-item :label="t('title')" prop="active_desc">
<div>
<el-input
v-model.trim="formData.active_desc"
clearable
:placeholder="t('titlePlaceholder')"
class="input-width"
:maxlength="20"
/>
<el-input v-model.trim="formData.active_desc" clearable :placeholder="t('titlePlaceholder')" class="input-width" :maxlength="20" />
<p class=" text-[14px] text-[#999]">{{ t('titleTip') }}</p>
</div>
</el-form-item>
<!-- 活动时间 -->
<el-form-item :label="t('activityTime')" prop="discount_time">
<div class="w-[180px]">
<el-date-picker
v-model="formData.discount_time"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
<el-date-picker v-model="formData.discount_time" type="datetimerange" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期"/>
</div>
</el-form-item>
<!-- 选择商品 -->
<el-form-item :label="t('selectProduct')" class="!m-0">
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="formData.goods_ids"
@goodsSelect="goodsSelect"
:min="1"
:max="99"
/>
<goods-select-popup ref="goodsSelectPopupRef" v-model="formData.goods_ids" @goodsSelect="goodsSelect" :min="1" :max="99" />
</el-form-item>
<el-form-item prop="goods_list"></el-form-item>
<el-form-item v-if="formData.goods_list.length&& goodsTable.data">
<div class="w-full sku_list">
<el-table
class="!w-[1400px] !max-w-[100%]"
:data="goodsTable.list"
size="large"
ref="goods_listTableRef"
@selection-change="handleSelectionChange"
>
<el-table class="!w-[1400px] !max-w-[100%]" :data="goodsTable.list" size="large" ref="goods_listTableRef" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ t('emptyData')}}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
:label="t('goodsSelectPopupGoodsInfo')"
min-width="300"
>
<el-table-column :label="t('goodsSelectPopupGoodsInfo')" min-width="300">
<template #default="{ row }">
<div class="flex items-center cursor-pointer">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.goods_cover_thumb_small"
class="w-[60px] h-[60px]"
:src="img(row.goods_cover_thumb_small)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.goods_cover_thumb_small" class="w-[60px] h-[60px]" :src="img(row.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.goods_name" class="multi-hidden">{{
row.goods_name
}}</span>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
<span :title="row.goods_name" class="multi-hidden">{{ row.goods_name }}</span>
<span class="text-primary text-[12px]">{{ row.goods_type_name }}</span>
</div>
</div>
</template>
@ -132,227 +72,111 @@
<el-table-column :label="t('discounts')" width="170">
<template #default="{ row,$index }">
<el-form-item
v-if="!row.goodsSku.sku_spec_format"
:key="row.goods_id"
:prop="'goods_list.' + row.index + '.discount_rate'"
:rules="[
{
<el-form-item v-if="!row.goodsSku.sku_spec_format" :key="row.goods_id" :prop="'goods_list.'+row.index + '.discount_rate'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
callback(t('discountsPlaceholder'))
} else if (
isNaN(value) ||
!regExp.number.test(value)
) {
} else if (isNaN(value) || !regExp.number.test(value)) {
callback(t('discountsTips'))
} else if (value <= 0) {
callback(t('discountsTipsTwo'))
} else if (value>9.9) {
callback(t('discountsTipsThree'))
} else {
callback()
callback();
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.discount_rate"
@blur="inputBlur(row, 'discount', row.index)"
clearable
placeholder="0.00"
maxlength="8"
/>
}
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.discount_rate" @blur="inputBlur(row,'discount',row.index)" clearable placeholder="0.00" maxlength="8" />
</el-form-item>
<el-form-item
:prop="'goods_list.' + row.index + '.valid'"
:rules="[
{
<el-form-item :prop="'goods_list.'+row.index + '.valid'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(t('skuDiscountSettingsPlaceholder'))
} else {
callback()
callback();
}
},
},
]"
v-else
>
<span
v-if="
row.valid &&
row.min_discount_rate != Infinity &&
row.max_discount_rate != -Infinity
"
>{{
row.min_discount_rate == row.max_discount_rate
? row.min_discount_rate
: row.min_discount_rate + '-' + row.max_discount_rate
}}</span
>
}
}]" v-else>
<span v-if="row.valid && row.min_discount_rate!=Infinity&&row.max_discount_rate!=-Infinity">{{ row.min_discount_rate==row.max_discount_rate?row.min_discount_rate:row.min_discount_rate+'-'+row.max_discount_rate }}</span>
<span v-else>--</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column :label="t('reduceMoney')" width="170">
<template #default="{ row,$index }">
<el-form-item
v-if="!row.goodsSku.sku_spec_format"
:key="row.goods_id"
:prop="'goods_list.' + row.index + '.reduce_money'"
:rules="[
{
<el-form-item v-if="!row.goodsSku.sku_spec_format" :key="row.goods_id" :prop="'goods_list.'+row.index + '.reduce_money'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
callback(t('reduceMoneyPlaceholder'))
} else if (
isNaN(value) ||
!regExp.digit.test(value)
) {
} else if (isNaN(value) || !regExp.digit.test(value)) {
callback(t('reduceMoneyTips'))
} else if (value <=0) {
callback(t('reduceMoneyTipsTwo'))
} else if (value >= parseFloat(row.goodsSku.price) ) {
callback(t('reduceMoneyTipsThree'))
} else {
callback()
callback();
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.reduce_money"
@blur="inputBlur(row, 'reduce', row.index)"
clearable
placeholder="0.00"
maxlength="8"
/>
}
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.reduce_money" @blur="inputBlur(row,'reduce',row.index)" clearable placeholder="0.00" maxlength="8" />
</el-form-item>
<el-form-item v-else>
<span
v-if="
row.valid &&
row.min_reduce_money != Infinity &&
row.max_reduce_money != -Infinity
"
>{{
row.min_reduce_money == row.max_reduce_money
? row.min_reduce_money
: row.min_reduce_money + '-' + row.max_reduce_money
}}</span
>
<span v-if="row.valid && row.min_reduce_money!=Infinity&&row.max_reduce_money!=-Infinity">{{ row.min_reduce_money==row.max_reduce_money?row.min_reduce_money:row.min_reduce_money+'-'+row.max_reduce_money }}</span>
<span v-else>--</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column :label="t('promotional')" width="170">
<template #default="{ row,$index }">
<el-form-item
v-if="!row.goodsSku.sku_spec_format"
:key="row.goods_id"
:prop="'goods_list.' + row.index + '.specify_price'"
:rules="[
{
<el-form-item v-if="!row.goodsSku.sku_spec_format" :key="row.goods_id" :prop="'goods_list.'+row.index + '.specify_price'" :rules="[{
trigger: 'blur',
validator: (rule: any, value: any, callback: any) => {
if (value.length == 0) {
callback(t('promotionalPlaceholder'))
} else if (
isNaN(value) ||
!regExp.digit.test(value)
) {
} else if (isNaN(value) || !regExp.digit.test(value)) {
callback(t('promotionalTips'))
} else if (value <=0 ) {
callback(t('promotionalTipsTwo'))
}else if (value >= parseFloat(row.goodsSku.price) ) {
callback(t('promotionalTipsThree'))
} else {
callback()
callback();
}
},
},
]"
class="sku-form-item-wrap"
>
<el-input
v-model.trim="row.specify_price"
clearable
@blur="inputBlur(row, 'specify', row.index)"
placeholder="0.00"
maxlength="8"
/>
}
}]" class="sku-form-item-wrap">
<el-input v-model.trim="row.specify_price" clearable @blur="inputBlur(row,'specify',row.index)" placeholder="0.00" maxlength="8" />
</el-form-item>
<el-form-item v-else>
<span
v-if="
row.valid &&
row.min_specify_price != Infinity &&
row.max_specify_price != -Infinity
"
>{{
row.min_specify_price == row.max_specify_price
? row.min_specify_price
: row.min_specify_price + '-' + row.max_specify_price
}}</span
>
<span v-if="row.valid && row.min_specify_price!=Infinity&&row.max_specify_price!=-Infinity">{{ row.min_specify_price==row.max_specify_price?row.min_specify_price:row.min_specify_price+'-'+row.max_specify_price }}</span>
<span v-else>--</span>
</el-form-item>
</template>
</el-table-column>
<el-table-column :label="t('discountType')" width="130">
<template #default="{ row }">
<span v-if="!row.goodsSku.sku_spec_format">{{
row.discount_type == 'discount'
? t('discounts')
: row.discount_type == 'reduce'
? t('reduceMoney')
: t('promotional')
}}</span>
<span v-if="!row.goodsSku.sku_spec_format">{{row.discount_type=='discount'?t('discounts'):row.discount_type=='reduce'?t('reduceMoney'):t('promotional')}}</span>
<el-form-item v-else>请在设置中查看</el-form-item>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
align="right"
min-width="160"
>
<el-table-column :label="t('operation')" align="right" min-width="160">
<template #default="{row}">
<!-- <el-button type="primary" link @click="enabledEvent(row)">{{ row.is_enabled?t('noEnabled'):t('enabled') }}</el-button> -->
<el-button
v-if="row.goodsSku.sku_spec_format"
type="primary"
link
@click="
skuDiscountSettingsEvent(formData.goods_list[row.index])
"
>
<el-button v-if="row.goodsSku.sku_spec_format" type="primary" link @click="skuDiscountSettingsEvent(formData.goods_list[row.index])">
{{t('skuDiscountSettings') }}
</el-button>
<el-button type="primary" link @click="deleteEvent(row.index)"
>{{ t('delete') }}
<el-button type="primary" link @click="deleteEvent(row.index)">{{t('delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<div
class="flex items-center justify-between mt-[15px] !w-[1400px] !max-w-[100%]"
>
<div class="flex items-center justify-between mt-[15px] !w-[1400px] !max-w-[100%]">
<div class="flex items-center mb-[15px]">
<el-checkbox
v-model="toggleCheckbox"
size="large"
class="!mr-[15px]"
@change="toggleChange"
:indeterminate="isIndeterminate"
>
<el-checkbox v-model="toggleCheckbox" size="large" class="!mr-[15px]" @change="toggleChange" :indeterminate="isIndeterminate">
<span>已选 {{ multipleSelection.length }} </span>
</el-checkbox>
@ -362,33 +186,14 @@
<el-option :label="t('reduceMoney')" value="reduce" />
<el-option :label="t('promotional')" value="specify" />
</el-select> -->
<el-input
v-model.trim="batchOperation.discountNumber"
clearable
:placeholder="
batchOperation.discount_type == 'discount'
? t('discounts')
: batchOperation.discount_type == 'reduce'
? t('reduceMoney')
: t('promotional')
"
class="!w-[130px] ml-[10px]"
maxlength="8"
/>
<el-button
class="ml-[10px]"
type="primary"
@click="saveBatch"
>{{ t('confirm') }}</el-button
>
<el-input v-model.trim="batchOperation.discountNumber" clearable
:placeholder="batchOperation.discount_type=='discount'?t('discounts'):batchOperation.discount_type=='reduce'?t('reduceMoney'):t('promotional')"
class="!w-[130px] ml-[10px]" maxlength="8" />
<el-button class="ml-[10px]" type="primary" @click="saveBatch">{{ t('confirm') }}</el-button>
</div>
<el-pagination
v-model:current-page="goodsTable.page"
v-model:page-size="goodsTable.limit"
layout="total, prev, pager, next, jumper"
:total="goodsTable.total"
@current-change="setGoodsList"
/>
<el-pagination v-model:current-page="goodsTable.page" v-model:page-size="goodsTable.limit"
layout="total, prev, pager, next, jumper" :total="goodsTable.total"
@current-change="setGoodsList" />
</div>
</div>
</el-form-item>
@ -400,9 +205,7 @@
<!-- 提交按钮 -->
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="onSave(formRef)">{{
t('save')
}}</el-button>
<el-button type="primary" @click="onSave(formRef)">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
</div>
</div>
@ -413,10 +216,7 @@
import {ref, computed, onMounted, reactive, nextTick} from 'vue'
import {t} from '@/lang'
import {useRoute, useRouter} from 'vue-router'
import {
getActiveDiscountInfo,
editActiveDiscount,
} from '@/addon/shop/api/marketing'
import { getActiveDiscountInfo, editActiveDiscount} from "@/addon/shop/api/marketing";
import {FormInstance, ElMessage} from 'element-plus'
import { ArrowLeft } from '@element-plus/icons-vue'
import {deepClone, img} from '@/utils/common'
@ -449,51 +249,40 @@ const formRef = ref<FormInstance>()
//
const regExp = {
number: /^\d{0,10}(.?\d{0,1})$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
digit: /^\d{0,10}(.?\d{0,2})$/
}
const formRules = computed(() => {
return {
active_name: [
{required: true, message: t('namePlaceholder'), trigger: 'blur'},
{ validator: noSpaceValidator, trigger: 'blur' },
{ validator: noSpaceValidator, trigger: 'blur' }
],
active_desc: [
{required: true, message: t('titlePlaceholder'), trigger: 'blur'},
{ validator: noSpaceValidator, trigger: 'blur' },
{ validator: noSpaceValidator, trigger: 'blur' }
],
goods_list: [
{
required: true,
message: t('selectProductPlaceholder'),
trigger: 'change',
},
{required: true, message: t('selectProductPlaceholder'), trigger: 'change'}
],
discount_time: [
{ required: true, validator: receiveTime, trigger: 'change' },
{required: true, validator: receiveTime, trigger: 'change'}
],
}
})
const noSpaceValidator = (rule: any, value: any, callback: any)=>{
if (value.trim() === '') {
return callback(new Error(t('noSpaceAllowed')))
return callback(new Error(t('noSpaceAllowed')));
}
callback() //
callback(); //
}
const receiveTime = (rule: any, value: any, callback: any) => {
if (
!formData.value.discount_time ||
(formData.value.discount_time &&
!formData.value.discount_time[0] &&
!formData.value.discount_time[1])
) {
if (!formData.value.discount_time || (formData.value.discount_time && !formData.value.discount_time[0] && !formData.value.discount_time[1])) {
callback(new Error(t('请选择活动时间')))
} else if (!formData.value.discount_time[0]) {
callback(new Error(t('请选择活动开始时间')))
} else if (!formData.value.discount_time[1]) {
callback(new Error(t('请选择活动结束时间')))
} else if (
formData.value.discount_time[1] <= formData.value.discount_time[0]
) {
} else if (formData.value.discount_time[1] <= formData.value.discount_time[0]) {
callback(new Error(t('活动结束时间不能小于等于活动开始时间')))
}
@ -512,6 +301,7 @@ const getActiveDiscountInfoFn = (id: number) => {
formData.value.goods_ids.push(el.goods_id)
formData.value.goods_list.push(el)
})
setGoodsList()
}
@ -521,10 +311,7 @@ const getActiveDiscountInfoFn = (id: number) => {
const validFn = (row: any) => {
if (row.discount_rate.length == 0) {
return false
} else if (
isNaN(row.discount_rate) ||
!regExp.number.test(row.discount_rate)
) {
} else if (isNaN(row.discount_rate) || !regExp.number.test(row.discount_rate)) {
return false
} else if (row.discount_rate <= 0) {
return false
@ -540,10 +327,7 @@ const validFn = (row: any) => {
return false
} else if (row.specify_price.length == 0) {
return false
} else if (
isNaN(row.specify_price) ||
!regExp.digit.test(row.specify_price)
) {
} else if (isNaN(row.specify_price) || !regExp.digit.test(row.specify_price)) {
return false
} else if (row.specify_price <= 0) {
return false
@ -559,23 +343,19 @@ const onSave = (formEl: FormInstance | undefined) => {
let el = formData.value.goods_list[i]
if (el.goodsSku.sku_spec_format) {
if (!el.valid) {
let page = Math.ceil(
i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit
)
let page = Math.ceil(i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit)
goodsTable.list = goodsTable.data[page - 1]
goodsTable.page = page
break
break;
} else {
el.sku_list = el.skuList
}
} else {
if (!validFn(el)) {
let page = Math.ceil(
i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit
)
let page = Math.ceil(i + 1 <= goodsTable.limit ? 1 : (i + 1) / goodsTable.limit)
goodsTable.list = goodsTable.data[page - 1]
goodsTable.page = page
break
break;
} else {
el.skuList[0].discount_rate = el.discount_rate
el.skuList[0].reduce_money = el.reduce_money
@ -594,25 +374,24 @@ const onSave = (formEl: FormInstance | undefined) => {
formData.value.start_time = formData.value.discount_time[0]
formData.value.end_time = formData.value.discount_time[1]
formData.value.goods_data = JSON.stringify(formData.value.goods_list)
editActiveDiscount(formData.value)
.then((res) => {
editActiveDiscount(formData.value).then(res => {
loading.value = false
history.back()
})
.catch(() => {
}).catch(() => {
loading.value = false
})
}
})
})
}
interface goodsTableInterface {
page: number
limit: number
total: number
data: any
list: Array<any>
page: number,
limit: number,
total: number,
data: any,
list: Array<any>,
}
const goodsTable = reactive<goodsTableInterface>({
@ -620,13 +399,12 @@ const goodsTable = reactive<goodsTableInterface>({
limit: 5,
total: 0,
data: [],
list: [],
list: []
})
const goodsSelect = (value: any) => {
if (formData.value.goods_list.length) {
let goods_list = deepClone(Object.values(value)).map(
(el: any, index: number) => {
let goods_list = deepClone(Object.values(value)).map((el: any, index: number) => {
if (!el.goodsSku.sku_spec_format) {
el.discount_type = 'discount'
el.discount_rate = ''
@ -643,14 +421,13 @@ const goodsSelect = (value: any) => {
el = Object.assign(el, v)//
el.index = index
}
})
return el
}
)
})
formData.value.goods_list = goods_list
} else {
formData.value.goods_list = deepClone(Object.values(value)).map(
(el: any, index: number) => {
formData.value.goods_list = deepClone(Object.values(value)).map((el: any, index: number) => {
if (!el.goodsSku.sku_spec_format) {
el.discount_type = 'discount'
el.discount_rate = ''
@ -663,8 +440,7 @@ const goodsSelect = (value: any) => {
el.index = index
el.valid = false
return el
}
)
})
}
setGoodsList()
if(formRef.value) formRef.value.validateField('goods_list').catch(()=>{})
@ -698,11 +474,11 @@ const setGoodsList = (page = 1) => {
}
//
const splitArray = (array: [], size: number) => {
var result = []
var result = [];
for (var i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size))
result.push(array.slice(i, i + size));
}
return result
return result;
}
//
const deleteEvent = (index: number) => {
@ -727,13 +503,13 @@ const skuSave = (row: any) => {
/*****批量设置 ****/
interface batchOperationInterface {
discount_type: any
discountNumber: any
discount_type: any,
discountNumber: any,
}
const batchOperation = ref<batchOperationInterface>({
discount_type: 'discount',
discountNumber: '',
discountNumber: ''
})
//
@ -757,10 +533,7 @@ const handleSelectionChange = (val: []) => {
multipleSelection.value = val
toggleCheckbox.value = false
if (
multipleSelection.value.length > 0 &&
multipleSelection.value.length < goodsTable.list.length
) {
if (multipleSelection.value.length > 0 && multipleSelection.value.length < goodsTable.list.length) {
isIndeterminate.value = true
} else {
isIndeterminate.value = false
@ -774,7 +547,7 @@ const saveBatch = () => {
if (!multipleSelection.value.length) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`,
message: `${t('batchEmptySelectedGoodsTips')}`
})
return
}
@ -782,28 +555,25 @@ const saveBatch = () => {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('discountsPlaceholder')}`,
message: `${t('discountsPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.number.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.number.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('discountsTips')}`,
message: `${t('discountsTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('discountsTipsTwo')}`,
message: `${t('discountsTipsTwo')}`
})
return
} else if (batchOperation.value.discountNumber > 9.9) {
ElMessage({
type: 'warning',
message: `${t('discountsTipsThree')}`,
message: `${t('discountsTipsThree')}`
})
return
}
@ -811,85 +581,68 @@ const saveBatch = () => {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyPlaceholder')}`,
message: `${t('reduceMoneyPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.digit.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.digit.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyTips')}`,
message: `${t('reduceMoneyTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('reduceMoneyTipsTwo')}`,
message: `${t('reduceMoneyTipsTwo')}`
})
return
}
} else {
if (batchOperation.value.discountNumber.length == 0) {
ElMessage({
type: 'warning',
message: `${t('promotionalPlaceholder')}`,
message: `${t('promotionalPlaceholder')}`
})
return
} else if (
isNaN(batchOperation.value.discountNumber) ||
!regExp.digit.test(batchOperation.value.discountNumber)
) {
} else if (isNaN(batchOperation.value.discountNumber) || !regExp.digit.test(batchOperation.value.discountNumber)) {
ElMessage({
type: 'warning',
message: `${t('promotionalTips')}`,
message: `${t('promotionalTips')}`
})
return
} else if (batchOperation.value.discountNumber <= 0) {
ElMessage({
type: 'warning',
message: `${t('promotionalTipsTwo')}`,
message: `${t('promotionalTipsTwo')}`
})
return
}
}
formData.value.goods_list.forEach((el: any, index: number) => {
multipleSelection.value.forEach((v: any) => {
if (v.goods_id === el.goods_id) {
if (!el.goodsSku.sku_spec_format) {
if (batchOperation.value.discount_type == 'discount') {
//
el.discount_rate = batchOperation.value.discountNumber + ''
//
el.specify_price = (
el.goodsSku.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
el.discount_price = (
el.goodsSku.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
el.specify_price = (el.goodsSku.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
el.discount_price = (el.goodsSku.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
//
el.reduce_money = (el.goodsSku.price - el.specify_price).toFixed(2)
} else if (batchOperation.value.discount_type == 'reduce') {
//
} else if (batchOperation.value.discount_type == 'reduce') {//
el.reduce_money = batchOperation.value.discountNumber + ''
el.specify_price = el.goodsSku.price - el.reduce_money.toFixed(2)
el.discount_price = (el.goodsSku.price - el.reduce_money).toFixed(2)
el.discount_rate = (
(el.specify_price / el.goodsSku.price) *
10
).toFixed(1)
} else {
//
el.discount_rate = (el.specify_price / el.goodsSku.price * 10).toFixed(1)
} else {//
el.specify_price = batchOperation.value.discountNumber + ''
el.discount_price = batchOperation.value.discountNumber + ''
el.reduce_money = (el.goodsSku.price - el.specify_price).toFixed(2)
el.discount_rate = (
(el.specify_price / el.goodsSku.price) *
10
).toFixed(1)
el.discount_rate = (el.specify_price / el.goodsSku.price * 10).toFixed(1)
}
el.discount_type = batchOperation.value.discount_type + ''
} else {
@ -899,47 +652,28 @@ const saveBatch = () => {
//
sku.discount_rate = batchOperation.value.discountNumber + ''
//
sku.specify_price = (
sku.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
sku.discount_price = (
sku.price *
(batchOperation.value.discountNumber / 10)
).toFixed(2)
sku.specify_price = (sku.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
sku.discount_price = (sku.price * (batchOperation.value.discountNumber / 10)).toFixed(2)
//
sku.reduce_money = (sku.price - sku.specify_price).toFixed(2)
} else if (batchOperation.value.discount_type == 'reduce') {
//
} else if (batchOperation.value.discount_type == 'reduce') {//
sku.reduce_money = batchOperation.value.discountNumber + ''
sku.specify_price = sku.price - sku.reduce_money.toFixed(2)
sku.discount_price = (sku.price - sku.reduce_money).toFixed(2)
sku.discount_rate = (
(sku.specify_price / sku.price) *
10
).toFixed(1)
} else {
//
sku.discount_rate = (sku.specify_price / sku.price * 10).toFixed(1)
} else {//
sku.specify_price = batchOperation.value.discountNumber + ''
sku.discount_price = batchOperation.value.discountNumber + ''
sku.reduce_money = (sku.price - sku.specify_price).toFixed(2)
sku.discount_rate = (
(sku.specify_price / sku.price) *
10
).toFixed(1)
sku.discount_rate = (sku.specify_price / sku.price * 10).toFixed(1)
}
sku.discount_type = batchOperation.value.discount_type + ''
}
})
let discount_rate_list = el.skuList
.filter((sku: any) => sku.is_enabled === 1)
.map((sku: any) => Number(sku.discount_rate))
let reduce_money_list = el.skuList
.filter((sku: any) => sku.is_enabled === 1)
.map((sku: any) => Number(sku.reduce_money))
let specify_price_list = el.skuList
.filter((sku: any) => sku.is_enabled === 1)
.map((sku: any) => Number(sku.specify_price))
let discount_rate_list = el.skuList.filter((sku:any)=>sku.is_enabled===1).map((sku:any)=>Number(sku.discount_rate))
let reduce_money_list = el.skuList.filter((sku:any)=>sku.is_enabled===1).map((sku:any)=>Number(sku.reduce_money))
let specify_price_list = el.skuList.filter((sku:any)=>sku.is_enabled===1).map((sku:any)=>Number(sku.specify_price))
el.max_discount_rate = Math.max(...discount_rate_list)
el.min_discount_rate = Math.min(...discount_rate_list)
el.max_reduce_money = Math.max(...reduce_money_list)
@ -961,63 +695,39 @@ const inputBlur = (row: any, discount_type: string, index: number) => {
if (discount_type == 'discount') {
if (row.discount_rate.length) {
//
row.specify_price = (
row.goodsSku.price *
(row.discount_rate / 10)
).toFixed(2)
row.discount_price = (
row.goodsSku.price *
(row.discount_rate / 10)
).toFixed(2)
row.specify_price = (row.goodsSku.price * (row.discount_rate / 10)).toFixed(2)
row.discount_price = (row.goodsSku.price * (row.discount_rate / 10)).toFixed(2)
//
row.reduce_money = (row.goodsSku.price - row.specify_price).toFixed(2)
if (formRef.value) {
formRef.value
.validateField('goods_list.' + index + '.specify_price')
.catch(() => {})
formRef.value
.validateField('goods_list.' + index + '.reduce_money')
.catch(() => {})
formRef.value.validateField('goods_list.' + index + '.specify_price').catch(() => {})
formRef.value.validateField('goods_list.' + index + '.reduce_money').catch(() => {})
}
}
} else if (discount_type == 'reduce') {
//
} else if (discount_type == 'reduce') {//
if (row.reduce_money.length) {
row.specify_price = (row.goodsSku.price - row.reduce_money).toFixed(2)
row.discount_price = (row.goodsSku.price - row.reduce_money).toFixed(2)
row.discount_rate = (
(row.specify_price / row.goodsSku.price) *
10
).toFixed(1)
row.discount_rate = (row.specify_price / row.goodsSku.price * 10).toFixed(1)
if (formRef.value) {
formRef.value
.validateField('goods_list.' + index + '.discount_rate')
.catch(() => {})
formRef.value
.validateField('goods_list.' + index + '.specify_price')
.catch(() => {})
formRef.value.validateField('goods_list.' + index + '.discount_rate').catch(() => {})
formRef.value.validateField('goods_list.' + index + '.specify_price').catch(() => {})
}
}
} else {
//
} else {//
if (row.specify_price.length) {
row.discount_price = row.specify_price + ''
row.reduce_money = (row.goodsSku.price - row.specify_price).toFixed(2)
row.discount_rate = (
(row.specify_price / row.goodsSku.price) *
10
).toFixed(1)
row.discount_rate = (row.specify_price / row.goodsSku.price * 10).toFixed(1)
if (formRef.value) {
formRef.value
.validateField('goods_list.' + index + '.discount_rate')
.catch(() => {})
formRef.value
.validateField('goods_list.' + index + '.reduce_money')
.catch(() => {})
formRef.value.validateField('goods_list.' + index + '.discount_rate').catch(() => {})
formRef.value.validateField('goods_list.' + index + '.reduce_money').catch(() => {})
}
}
}
row.discount_type = discount_type + ''
}
const back = () => {
router.push('/shop/marketing/discount/list')
@ -1035,7 +745,7 @@ input::-webkit-inner-spin-button {
margin: 0;
}
input[type='number'] {
input[type="number"] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
-o-appearance: textfield;

198
admin/src/addon/shop/views/marketing/discount/list.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="handleChange">
@ -9,88 +10,36 @@
</div>
<!-- 搜索 -->
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="tableData.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="active_name">
<el-input
v-model.trim="tableData.searchParam.active_name"
:placeholder="t('namePlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.active_name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="active_status">
<el-select
v-model="tableData.searchParam.active_status"
clearable
:placeholder="t('statusPlaceholder')"
class="input-item"
>
<el-option
v-for="(item, key) in activeStatusOption"
:key="key"
:label="item"
:value="key"
></el-option>
<el-form-item :label="t('status')" prop='active_status'>
<el-select v-model="tableData.searchParam.active_status" clearable :placeholder="t('statusPlaceholder')" class="input-item">
<el-option v-for="(item, key) in activeStatusOption" :key="key" :label="item" :value="key"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadDiscountList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadDiscountList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 列表 -->
<div>
<el-table
:data="tableData.data"
size="large"
v-loading="tableData.loading"
>
<el-table :data="tableData.data" size="large" v-loading="tableData.loading">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="active_name"
:label="t('name')"
min-width="130"
/>
<el-table-column
prop="active_desc"
:label="t('title')"
min-width="130"
/>
<el-table-column
prop="active_status_name"
:label="t('status')"
min-width="130"
/>
<el-table-column
prop="active_order_money"
:label="t('paymentAmount')"
min-width="130"
/>
<el-table-column
prop="active_order_num"
:label="t('orderCount')"
min-width="130"
/>
<el-table-column
prop="active_member_num"
:label="t('memberCount')"
min-width="130"
/>
<el-table-column prop="active_name" :label="t('name')" min-width="130" />
<el-table-column prop="active_desc" :label="t('title')" min-width="130" />
<el-table-column prop="active_status_name" :label="t('status')" min-width="130" />
<el-table-column prop="active_order_money" :label="t('paymentAmount')" min-width="130" />
<el-table-column prop="active_order_num" :label="t('orderCount')" min-width="130" />
<el-table-column prop="active_member_num" :label="t('memberCount')" min-width="130" />
<el-table-column :label="t('discountTime')" min-width="150">
<template #default="{ row }">
<div>
@ -99,56 +48,20 @@
</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="160"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="160">
<template #default="{ row }">
<el-button
v-if="
row.active_status == 'not_active' ||
row.active_status == 'active'
"
type="primary"
link
@click="editEvent(row.active_id)"
>{{ t('edit') }}</el-button
>
<el-button
type="primary"
link
@click="detailEvent(row.active_id)"
>{{ t('detail') }}</el-button
>
<el-button
v-if="row.active_status == 'active'"
type="primary"
link
@click="closeEvent(row.active_id)"
>{{ t('close') }}</el-button
>
<el-button
v-if="row.active_status != 'active'"
type="primary"
link
@click="deleteEvent(row.active_id)"
>{{ t('delete') }}</el-button
>
<el-button v-if="row.active_status=='not_active'||row.active_status=='active'" type="primary" link @click="editEvent(row.active_id)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="detailEvent(row.active_id)">{{ t('detail') }}</el-button>
<el-button v-if="row.active_status=='active'" type="primary" link @click="closeEvent(row.active_id)">{{ t('close') }}</el-button>
<el-button v-if="row.active_status!='active'" type="primary" link @click="deleteEvent(row.active_id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="loadDiscountList()"
@current-change="loadDiscountList"
/>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
@size-change="loadDiscountList()" @current-change="loadDiscountList" />
</div>
</div>
</el-card>
@ -160,15 +73,10 @@
import { reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessageBox, FormInstance } from 'element-plus'
import {
getActiveDiscountPageList,
getActiveDiscountStatusList,
closeActiveDiscount,
deleteActiveDiscount,
} from '@/addon/shop/api/marketing'
import { getActiveDiscountPageList,getActiveDiscountStatusList,closeActiveDiscount,deleteActiveDiscount } from "@/addon/shop/api/marketing";
import { t } from '@/lang'
import discountDetail from '@/addon/shop/views/marketing/discount/components/discount-detail.vue'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const router = useRouter()
const route = useRoute()
@ -183,8 +91,8 @@ const tableData = reactive({
data: [],
searchParam: {
active_name: '',
active_status: '',
},
active_status:''
}
})
const searchFormRef = ref<FormInstance>()
const loadDiscountList = (page: number = 1) => {
@ -194,23 +102,17 @@ const loadDiscountList = (page: number = 1) => {
getActiveDiscountPageList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
})
.then((res) => {
...tableData.searchParam
}).then(res => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
setTablePageStorage(
tableData.page,
tableData.limit,
tableData.searchParam
)
})
.catch(() => {
setTablePageStorage(tableData.page, tableData.limit, tableData.searchParam);
}).catch(() => {
tableData.loading = false
})
}
loadDiscountList(getTablePageStorage(tableData.searchParam).page)
loadDiscountList(getTablePageStorage(tableData.searchParam).page);
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
@ -219,7 +121,7 @@ const resetForm = (formEl: FormInstance | undefined) => {
//
const activeStatusOption = ref({})
const getActiveDiscountStatusListFn=()=>{
getActiveDiscountStatusList().then((res) => {
getActiveDiscountStatusList().then(res=>{
activeStatusOption.value = res.data
})
}
@ -231,9 +133,9 @@ const handleChange = () => {
//
const discountDetailDialog: Record<string, any> | null = ref(null)
const detailEvent=(id:number)=>{
let data = { id: id }
discountDetailDialog.value.setFormData(data)
discountDetailDialog.value.showDialog = true
let data = {id: id};
discountDetailDialog.value.setFormData(data);
discountDetailDialog.value.showDialog = true;
}
//
const editEvent = (id:number)=>{
@ -241,30 +143,32 @@ const editEvent = (id: number) => {
}
//
const closeEvent = (id:number)=>{
ElMessageBox.confirm(t('closeTips'), t('warning'), {
ElMessageBox.confirm(t('closeTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
closeActiveDiscount(id)
.then(() => {
type: 'warning'
}
).then(() => {
closeActiveDiscount(id).then(() => {
loadDiscountList()
}).catch(() => {
})
.catch(() => {})
})
}
//
const deleteEvent = (id:number)=>{
ElMessageBox.confirm(t('deleteTips'), t('warning'), {
ElMessageBox.confirm(t('deleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteActiveDiscount(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteActiveDiscount(id).then(() => {
loadDiscountList()
}).catch(() => {
})
.catch(() => {})
})
}
</script>

175
admin/src/addon/shop/views/marketing/exchange/components/goods-select-popup.vue

@ -1,146 +1,81 @@
<template>
<el-dialog
v-model="showDialog"
:title="t('goodsSelectPopupSelectGoodsDialog')"
width="60%"
:destroy-on-close="true"
>
<el-dialog v-model="showDialog" :title="t('goodsSelectPopupSelectGoodsDialog')" width="60%" :destroy-on-close="true">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('goodsSelectPopupGoodsName')" prop="active_name">
<el-input
v-model.trim="tableData.searchParam.name"
:placeholder="t('goodsSelectPopupGoodsNamePlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.name" :placeholder="t('goodsSelectPopupGoodsNamePlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select
v-model="tableData.searchParam.status"
clearable
:placeholder="t('goodsSelectPopupGoodsStatusPlaceholder')"
class="input-item"
>
<el-option
v-for="(item, key) in statusOption"
:key="key"
:label="item"
:value="key"
></el-option>
<el-form-item :label="t('status')" prop='status'>
<el-select v-model="tableData.searchParam.status" clearable :placeholder="t('goodsSelectPopupGoodsStatusPlaceholder')" class="input-item">
<el-option v-for="(item, key) in statusOption" :key="key" :label="item" :value="key"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadExchangeGoodsList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadExchangeGoodsList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<div class="mt-[10px]">
<el-table
:data="tableData.data"
ref="tableRef"
row-key="id"
size="large"
v-loading="tableData.loading"
@selection-change="handleSelectionChange"
>
<el-table :data="tableData.data" ref="tableRef" row-key="id" size="large" v-loading="tableData.loading" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column type="selection" width="55" reserve-selection />
<el-table-column
:label="t('goodsSelectPopupGoodsInfo')"
min-width="130"
>
<el-table-column :label="t('goodsSelectPopupGoodsInfo')" min-width="130">
<template #default="{ row }">
<div class="flex items-center cursor-pointer">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.image"
class="w-[60px] h-[60px]"
:src="img(row.image)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.image" class="w-[60px] h-[60px]" :src="img(row.image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.names" class="multi-hidden">{{
row.names
}}</span>
<span :title="row.names" class="multi-hidden">{{ row.names }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column :label="t('goodsSelectPopupPrice')" min-width="130">
<template #default="{ row }">
<p v-if="row.point">
{{ row.point }}{{ t('goodsSelectPopupGoodsPointUnit') }}
</p>
<p v-if="row.price">
{{ row.price }}{{ t('goodsSelectPopupGoodsPriceUnit') }}
</p>
<p v-if="row.point">{{ row.point }}{{ t('goodsSelectPopupGoodsPointUnit') }}</p>
<p v-if="row.price">{{ row.price }}{{ t('goodsSelectPopupGoodsPriceUnit') }}</p>
</template>
</el-table-column>
<el-table-column
:label="t('goodsSelectPopupStock')"
min-width="130"
align="right"
>
<el-table-column :label="t('goodsSelectPopupStock')" min-width="130" align="right">
<template #default="{ row }">
<span>{{ row.stock }}</span>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="loadExchangeGoodsList()"
@current-change="loadExchangeGoodsList"
/>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
@size-change="loadExchangeGoodsList()" @current-change="loadExchangeGoodsList" />
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="save">{{ t('confirm') }}</el-button>
<el-button @click="showDialog = false">{{ t("cancel") }}</el-button>
<el-button type="primary" @click="save">{{ t("confirm") }}</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import { img } from '@/utils/common'
import {
getActiveExchangePageList,
getActiveExchangeStatus,
} from '@/addon/shop/api/marketing'
import { ref, nextTick, reactive } from 'vue'
import {t} from "@/lang";
import {img} from "@/utils/common";
import {getActiveExchangePageList, getActiveExchangeStatus} from "@/addon/shop/api/marketing";
import {ref, nextTick, reactive} from "vue";
import {FormInstance, ElMessage} from 'element-plus'
const showDialog = ref(false)
const tableRef = ref()
const showDialog = ref(false);
const tableRef = ref();
//
const tableData = reactive({
page: 1,
@ -151,19 +86,19 @@ const tableData = reactive({
searchParam: {
name: '',
status: '',
create_time: [],
},
create_time: []
}
})
const prop = defineProps({
max: {
type: Number,
default: 0,
default: 0
},
min: {
type: Number,
default: 0,
},
})
default: 0
}
});
const searchFormRef = ref<FormInstance>()
const loadExchangeGoodsList = (page: number = 1) => {
tableData.loading = true
@ -172,9 +107,8 @@ const loadExchangeGoodsList = (page: number = 1) => {
getActiveExchangePageList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
})
.then((res: any) => {
...tableData.searchParam
}).then((res: any) => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
@ -182,54 +116,55 @@ const loadExchangeGoodsList = (page: number = 1) => {
nextTick(() => {
tableData.data.forEach((el: any) => {
if (ids.value.includes(el.id)) {
tableRef.value.toggleRowSelection(el, true)
tableRef.value.toggleRowSelection(el, true);
}
})
})
}
})
.catch(() => {
}).catch(() => {
tableData.loading = false
})
}
//
const statusOption = ref([])
const getActiveExchangeStatusFn = () => {
getActiveExchangeStatus().then((res) => {
getActiveExchangeStatus().then(res => {
statusOption.value = res.data
})
}
getActiveExchangeStatusFn()
const emit = defineEmits(['select'])
const emit = defineEmits(["select"]);
const ids = ref<Array<any>>([])
const show = (data: any) => {
ids.value = data
showDialog.value = true
showDialog.value = true;
loadExchangeGoodsList()
}
};
//
const multipleSelection: any = ref([])
const multipleSelection: any = ref([]);
//
const handleSelectionChange = (val: []) => {
multipleSelection.value = val
}
multipleSelection.value = val;
};
const save = () => {
if (prop.min && multipleSelection.value.length < prop.min) {
ElMessage({
type: 'warning',
message: `${t('goodsSelectPopupGoodsMinTip')}${prop.min}${t('goodsSelectPopupPiece')}`,
})
return
});
return;
}
emit('select', multipleSelection.value)
showDialog.value = false
if (tableRef.value) tableRef.value.clearSelection()
}
emit("select", multipleSelection.value);
showDialog.value = false;
if (tableRef.value) tableRef.value.clearSelection();
};
defineExpose({
show,
})
show
});
</script>
<style lang="scss" scoped></style>

115
admin/src/addon/shop/views/marketing/exchange/components/goods-sku-select.vue

@ -1,73 +1,34 @@
<template>
<div>
<el-dialog
v-model="showDialog"
:title="t('goodsSkuTitle')"
width="60%"
:destroy-on-close="true"
>
<el-table
class="!max-w-[100%]"
:data="tableData"
size="large"
ref="tableRef"
@selection-change="handleSelectionChange"
>
<el-dialog v-model="showDialog" :title="t('goodsSkuTitle')" width="60%" :destroy-on-close="true">
<el-table class="!max-w-[100%]" :data="tableData" size="large" ref="tableRef" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column
:label="t('goodsSelectPopupGoodsInfo')"
min-width="300"
>
<el-table-column :label="t('goodsSelectPopupGoodsInfo')" min-width="300">
<template #default="{ row }">
<div class="flex items-center cursor-pointer">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.sku_image"
class="w-[60px] h-[60px]"
:src="img(row.sku_image)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.sku_image" class="w-[60px] h-[60px]" :src="img(row.sku_image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.sku_name" class="multi-hidden">{{
row.sku_name || row.goods?.goods_name
}}</span>
<span :title="row.sku_name" class="multi-hidden">{{ row.sku_name||row.goods?.goods_name }}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column
prop="goods_price"
:label="t('price')"
min-width="170"
/>
<el-table-column
prop="goods_stock"
:label="t('goodsStock')"
min-width="170"
/>
<el-table-column prop="goods_price" :label="t('price')" min-width="170" />
<el-table-column prop="goods_stock" :label="t('goodsStock')" min-width="170" />
</el-table>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="save">{{ t('confirm') }}</el-button>
<el-button @click="showDialog = false">{{ t("cancel") }}</el-button>
<el-button type="primary" @click="save">{{ t("confirm") }}</el-button>
</span>
</template>
</el-dialog>
@ -75,29 +36,29 @@
</template>
<script lang="ts" setup>
import { t } from '@/lang'
import { img } from '@/utils/common'
import {t} from "@/lang";
import {img} from "@/utils/common";
import {getGoodsSkuList} from '@/addon/shop/api/goods'
import { ref, nextTick } from 'vue'
import {ref, nextTick} from "vue";
const showDialog = ref(false)
const showDialog = ref(false);
const loading = ref(false)
const tableRef = ref()
const tableData = ref()
const tableRef = ref();
const tableData = ref();
const prop = defineProps({
id: {
type: String,
default: '',
},
})
const emit = defineEmits(['skuSelect'])
});
const emit = defineEmits(["skuSelect"]);
const show = (data: any) => {
showDialog.value = true
showDialog.value = true;
loading.value = true
let sku_ids = data.map((el: any) => el.sku_id)
let sku_ids = data.map((el: any) => el.sku_id);
getGoodsSkuList({
goods_id: prop.id,
}).then((res) => {
goods_id: prop.id
}).then(res => {
tableData.value = res.data.map((el: any) => {
el.goods_stock = el.stock + ''
el.goods_price = el.price + ''
@ -109,30 +70,32 @@ const show = (data: any) => {
})
nextTick(() => {
res.data.forEach((el: any) => {
tableRef.value?.toggleRowSelection(el, false)
tableRef.value?.toggleRowSelection(el, false);
if (sku_ids.includes(el.sku_id))
tableRef.value.toggleRowSelection(el, true)
})
tableRef.value.toggleRowSelection(el, true);
});
loading.value = false
})
})
}
};
//
const multipleSelection: any = ref([])
const multipleSelection: any = ref([]);
//
const handleSelectionChange = (val: []) => {
multipleSelection.value = val
}
multipleSelection.value = val;
};
const save = () => {
emit('skuSelect', multipleSelection.value)
showDialog.value = false
if (tableRef.value) tableRef.value.clearSelection()
}
emit("skuSelect", multipleSelection.value);
showDialog.value = false;
if (tableRef.value) tableRef.value.clearSelection();
};
defineExpose({
show,
})
show
});
</script>
<style lang="scss" scoped></style>

727
admin/src/addon/shop/views/marketing/exchange/goods_add.vue

File diff suppressed because it is too large

684
admin/src/addon/shop/views/marketing/exchange/goods_edit.vue

File diff suppressed because it is too large

199
admin/src/addon/shop/views/marketing/exchange/goods_list.vue

@ -1,6 +1,7 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="handleChange">
@ -9,63 +10,29 @@
</div>
<!-- 搜索 -->
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="tableData.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('goodsName')" prop="names">
<el-input
v-model.trim="tableData.searchParam.names"
:placeholder="t('goodsNamePlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.names" :placeholder="t('goodsNamePlaceholder')" />
</el-form-item>
<el-form-item :label="t('status')" prop="status">
<el-select
v-model="tableData.searchParam.status"
clearable
:placeholder="t('statusPlaceholder')"
class="input-item"
>
<el-option
v-for="(item, key) in statusOption"
:key="key"
:label="item"
:value="key"
></el-option>
<el-form-item :label="t('status')" prop='status'>
<el-select v-model="tableData.searchParam.status" clearable :placeholder="t('statusPlaceholder')" class="input-item">
<el-option v-for="(item, key) in statusOption" :key="key" :label="item" :value="key"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker
v-model="tableData.searchParam.create_time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
<el-date-picker v-model="tableData.searchParam.create_time" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadExchangeGoodsList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadExchangeGoodsList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 列表 -->
<div class="mt-[10px]">
<el-table
:data="tableData.data"
size="large"
v-loading="tableData.loading"
>
<el-table :data="tableData.data" size="large" v-loading="tableData.loading">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
</template>
@ -73,35 +40,18 @@
<el-table-column :label="t('goods')" min-width="130" >
<template #default="{ row }">
<div class="flex items-center cursor-pointer">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.goods_cover_thumb_small"
class="w-[60px] h-[60px]"
:src="img(row.goods_cover_thumb_small)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.goods_cover_thumb_small" class="w-[60px] h-[60px]" :src="img(row.goods_cover_thumb_small)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.names" class="multi-hidden">{{
row.names
}}</span>
<span :title="row.names" class="multi-hidden">{{ row.names }}</span>
</div>
</div>
</template>
@ -117,67 +67,27 @@
<span>{{ row.total_exchange_num }}/{{ row.stock }}</span>
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="150"
>
<el-table-column prop="create_time" :label="t('createTime')" min-width="150">
<template #default="{ row }">
<div>{{ row.create_time }}</div>
</template>
</el-table-column>
<el-table-column
prop="status_name"
:label="t('status')"
min-width="130"
/>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="160"
>
<el-table-column prop="status_name" :label="t('status')" min-width="130" />
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="160">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row.id)">{{
t('edit')
}}</el-button>
<el-button type="primary" link @click="spreadEvent(row)">{{
t('spreadGoods')
}}</el-button>
<el-button
v-if="row.status"
type="primary"
link
@click="statusEvent(row.id, 0)"
>{{ t('down') }}</el-button
>
<el-button
v-else
type="primary"
link
@click="statusEvent(row.id, 1)"
>{{ t('up') }}</el-button
>
<el-button
v-if="!row.status"
type="primary"
link
@click="deleteEvent(row.id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row.id)">{{ t('edit') }}</el-button>
<el-button type="primary" link @click="spreadEvent(row)">{{ t('spreadGoods') }}</el-button>
<el-button v-if="row.status" type="primary" link @click="statusEvent(row.id,0)">{{ t('down') }}</el-button>
<el-button v-else type="primary" link @click="statusEvent(row.id,1)">{{ t('up') }}</el-button>
<el-button v-if="!row.status" type="primary" link @click="deleteEvent(row.id)">{{ t('delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="loadExchangeGoodsList()"
@current-change="loadExchangeGoodsList"
/>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper" :total="tableData.total"
@size-change="loadExchangeGoodsList()" @current-change="loadExchangeGoodsList" />
</div>
</div>
</el-card>
@ -194,12 +104,7 @@ import { img, setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { useRoute, useRouter } from 'vue-router'
import { ElMessageBox, FormInstance } from 'element-plus'
import goodsSpreadPopup from '@/addon/shop/views/goods/components/goods-spread-popup.vue'
import {
getActiveExchangePageList,
deleteActiveExchange,
editActiveExchangeStatus,
getActiveExchangeStatus,
} from '@/addon/shop/api/marketing'
import { getActiveExchangePageList, deleteActiveExchange, editActiveExchangeStatus, getActiveExchangeStatus } from '@/addon/shop/api/marketing'
const route = useRoute()
const router = useRouter()
@ -215,8 +120,8 @@ const tableData = reactive({
searchParam: {
names: '',
status: '',
create_time: [],
},
create_time: []
}
})
const searchFormRef = ref<FormInstance>()
const loadExchangeGoodsList = (page: number = 1) => {
@ -226,27 +131,21 @@ const loadExchangeGoodsList = (page: number = 1) => {
getActiveExchangePageList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
})
.then((res) => {
...tableData.searchParam
}).then(res => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
setTablePageStorage(
tableData.page,
tableData.limit,
tableData.searchParam
)
})
.catch(() => {
setTablePageStorage(tableData.page, tableData.limit, tableData.searchParam);
}).catch(() => {
tableData.loading = false
})
}
loadExchangeGoodsList(getTablePageStorage(tableData.searchParam).page)
loadExchangeGoodsList(getTablePageStorage(tableData.searchParam).page);
//
const statusOption = ref([])
const getActiveExchangeStatusFn = () => {
getActiveExchangeStatus().then((res) => {
getActiveExchangeStatus().then(res => {
statusOption.value = res.data
})
}
@ -268,30 +167,32 @@ const spreadEvent = (data: any) => {
}
//
const statusEvent = (id:number, status:number) => {
ElMessageBox.confirm(status ? t('upTips') : t('downTips'), t('warning'), {
ElMessageBox.confirm(status ? t('upTips') : t('downTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
editActiveExchangeStatus({ id, status })
.then(() => {
type: 'warning'
}
).then(() => {
editActiveExchangeStatus({ id, status }).then(() => {
loadExchangeGoodsList()
}).catch(() => {
})
.catch(() => {})
})
}
//
const deleteEvent = (id:number) => {
ElMessageBox.confirm(t('deleteTips'), t('warning'), {
ElMessageBox.confirm(t('deleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteActiveExchange(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteActiveExchange(id).then(() => {
loadExchangeGoodsList()
}).catch(() => {
})
.catch(() => {})
})
}
const resetForm = (formEl: FormInstance | undefined) => {

172
admin/src/addon/shop/views/marketing/exchange/order_list.vue

@ -1,89 +1,43 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="orderTable.searchParam"
ref="searchFormRef"
>
<el-form-item :label="t('orderInfo')" prop="search_name">
<el-select
v-model="orderTable.searchParam.search_type"
clearable
class="input-item"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="orderTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('orderInfo')" prop='search_name'>
<el-select v-model="orderTable.searchParam.search_type" clearable class="input-item">
<el-option :label="t('orderNo')" value="order_no"></el-option>
<el-option
:label="t('outTradeNo')"
value="out_trade_no"
></el-option>
<el-option :label="t('outTradeNo')" value="out_trade_no"></el-option>
</el-select>
<el-input
class="input-item ml-3"
v-model.trim="orderTable.searchParam.search_name"
/>
<el-input class="input-item ml-3" v-model.trim="orderTable.searchParam.search_name" />
</el-form-item>
<el-form-item :label="t('payType')" prop="pay_type">
<el-select
v-model="orderTable.searchParam.pay_type"
clearable
class="input-item"
>
<el-option
v-for="(item, index) in payTypeData"
:key="index"
:label="item.name"
:value="item.key"
></el-option>
<el-form-item :label="t('payType')" prop='pay_type'>
<el-select v-model="orderTable.searchParam.pay_type" clearable class="input-item">
<el-option v-for="(item, index) in payTypeData" :key="index" :label="item.name" :value="item.key"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="t('fromType')" prop="order_from">
<el-select
v-model="orderTable.searchParam.order_from"
clearable
class="input-item"
>
<el-option
v-for="(item, index) in orderFromData"
:key="index"
:label="item"
:value="index"
></el-option>
<el-form-item :label="t('fromType')" prop='order_from'>
<el-select v-model="orderTable.searchParam.order_from" clearable class="input-item">
<el-option v-for="(item, index) in orderFromData" :key="index" :label="item" :value="index"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="t('createTime')" prop="create_time">
<el-date-picker
v-model="orderTable.searchParam.create_time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
<el-date-picker v-model="orderTable.searchParam.create_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item :label="t('payTime')" prop="pay_time">
<el-date-picker
v-model="orderTable.searchParam.pay_time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
<el-date-picker v-model="orderTable.searchParam.pay_time" type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')"
:end-placeholder="t('endDate')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadOrderList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadOrderList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
@ -96,11 +50,7 @@
<el-tab-pane :label="t('closed')" name="-1"></el-tab-pane>
</el-tabs>
<el-table
:data="orderTable.data"
size="large"
v-loading="orderTable.loading"
>
<el-table :data="orderTable.data" size="large" v-loading="orderTable.loading">
<template #empty>
<span>{{ !orderTable.loading ? t('emptyData') : '' }}</span>
</template>
@ -114,18 +64,8 @@
<template #default="{ row }">
<div class="flex cursor-pointer" v-for="item in row.order_goods">
<div class="flex items-center min-w-[50px] mr-[10px]">
<img
class="w-[50px] h-[50px]"
v-if="item.goods_image_thumb_small"
:src="img(item.goods_image_thumb_small)"
alt=""
/>
<img
class="w-[50px] h-[50px]"
v-else
src="@/addon/shop/assets/goods_default.png"
alt=""
/>
<img class="w-[50px] h-[50px]" v-if="item.goods_image_thumb_small" :src="img(item.goods_image_thumb_small)" alt="">
<img class="w-[50px] h-[50px]" v-else src="@/addon/shop/assets/goods_default.png" alt="">
</div>
<div class="flex flex-col">
<p class="multi-hidden text-[14px]">{{ item.goods_name }}</p>
@ -139,14 +79,10 @@
<div class="flex flex-col" v-for="item in row.order_goods">
<span v-if="row.activity_type == 'exchange'" class="text-[14px]">
{{ item.extend.point }}{{ t('point') }}
<span v-if="parseFloat(item.extend.price)"
>+{{ item.extend.price }}</span
>
<span v-if="parseFloat(item.extend.price)">+{{ item.extend.price }}</span>
</span>
<span v-else class="text-[13px]">{{ item.price }}</span>
<span class="text-[13px] mt-[5px]"
>{{ item.num }}{{ t('piece') }}</span
>
<span class="text-[13px] mt-[5px]">{{ item.num }}{{ t('piece') }}</span>
</div>
</template>
</el-table-column>
@ -154,9 +90,7 @@
<template #default="{ row }">
<span v-if="row.activity_type == 'exchange'" class="text-[14px]">
{{ row.point }}{{ t('point') }}
<span v-if="parseFloat(row.order_money)"
>+{{ row.order_money }}</span
>
<span v-if="parseFloat(row.order_money)">+{{ row.order_money }}</span>
</span>
<span v-else class="text-[14px]">{{ row.order_money }}</span>
</template>
@ -171,30 +105,19 @@
<span class="text-[14px]">{{ row.status_name.name }}</span>
</template>
</el-table-column>
<el-table-column
:label="t('操作')"
fixed="right"
align="right"
min-width="100"
>
<el-table-column :label="t('操作')" fixed="right" align="right" min-width="100">
<template #default="{ row }">
<el-button type="primary" link @click="detailEvent(row)">{{
t('info')
}}</el-button>
<el-button type="primary" link @click="detailEvent(row)">{{ t('info') }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="orderTable.page"
v-model:page-size="orderTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="orderTable.total"
@size-change="loadOrderList()"
@current-change="loadOrderList"
/>
<el-pagination v-model:current-page="orderTable.page" v-model:page-size="orderTable.limit"
layout="total, sizes, prev, pager, next, jumper" :total="orderTable.total"
@size-change="loadOrderList()" @current-change="loadOrderList" />
</div>
</el-card>
</div>
</template>
@ -202,12 +125,7 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getOrderList,
getOrderStatus,
getOrderPayType,
getOrderFrom,
} from '@/addon/shop/api/order'
import { getOrderList, getOrderStatus, getOrderPayType, getOrderFrom } from '@/addon/shop/api/order'
import { img,setTablePageStorage,getTablePageStorage } from '@/utils/common'
import { FormInstance } from 'element-plus'
import { useRouter, useRoute } from 'vue-router'
@ -234,7 +152,7 @@ interface OrderTable {
total: number
loading: boolean
data: any[]
searchParam: any
searchParam: any,
}
const orderTable = reactive<OrderTable>({
page: 1,
@ -250,8 +168,8 @@ const orderTable = reactive<OrderTable>({
status: '',
create_time: [],
pay_time: [],
activity_type: 'exchange',
},
activity_type:'exchange'
}
})
const searchFormRef = ref<FormInstance>()
@ -266,9 +184,8 @@ const loadOrderList = (page: number = 1) => {
getOrderList({
page: orderTable.page,
limit: orderTable.limit,
...orderTable.searchParam,
})
.then((res) => {
...orderTable.searchParam
}).then(res => {
orderTable.loading = false
orderTable.data = res.data.data.map((el: any) => {
el.order_goods.forEach((v: any) => {
@ -277,17 +194,12 @@ const loadOrderList = (page: number = 1) => {
return el
})
orderTable.total = res.data.total
setTablePageStorage(
orderTable.page,
orderTable.limit,
orderTable.searchParam
)
})
.catch(() => {
setTablePageStorage(orderTable.page, orderTable.limit, orderTable.searchParam);
}).catch(() => {
orderTable.loading = false
})
}
loadOrderList(getTablePageStorage(orderTable.searchParam).page)
loadOrderList(getTablePageStorage(orderTable.searchParam).page);
const handleClick = (event: any) => {
orderTable.searchParam.status = event

200
admin/src/addon/shop/views/marketing/goods_rank/components/rank-select-popup.vue

@ -10,103 +10,40 @@
</div>
</slot>
</div>
<el-dialog
v-model="showDialog"
:name="t('rankSelect')"
width="1000px"
:close-on-press-escape="false"
:destroy-on-close="true"
:close-on-click-modal="false"
>
<el-form
:inline="true"
:model="rankTable.searchParam"
ref="searchFormRef"
>
<el-form-item
:label="t('rankName')"
prop="keyword"
class="form-item-wrap"
>
<el-input
v-model.trim="rankTable.searchParam.name"
:placeholder="t('rankNamePlaceholder')"
maxlength="60"
/>
<el-dialog v-model="showDialog" :name="t('rankSelect')" width="1000px" :close-on-press-escape="false" :destroy-on-close="true" :close-on-click-modal="false">
<el-form :inline="true" :model="rankTable.searchParam" ref="searchFormRef">
<el-form-item :label="t('rankName')" prop="keyword" class="form-item-wrap">
<el-input v-model.trim="rankTable.searchParam.name" :placeholder="t('rankNamePlaceholder')" maxlength="60" />
</el-form-item>
<el-form-item class="form-item-wrap">
<el-button type="primary" @click="loadRankList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadRankList()">{{ t('search') }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t('reset') }}</el-button>
</el-form-item>
</el-form>
<el-table
:data="rankTable.data"
size="large"
v-loading="rankTable.loading"
ref="rankListTableRef"
max-height="400"
@select="handleSelectChange"
@select-all="handleSelectAllChange"
>
<el-table :data="rankTable.data" size="large" v-loading="rankTable.loading" ref="rankListTableRef" max-height="400" @select="handleSelectChange" @select-all="handleSelectAllChange">
<template #empty>
<span>{{ !rankTable.loading ? t('emptyData') : '' }}</span>
</template>
<!-- 多选框 -->
<el-table-column type="selection" width="55" />
<el-table-column prop="name" :label="t('rankName')" min-width="130" />
<el-table-column
prop="show_goods_num"
:label="t('showGoodsNum')"
min-width="130"
/>
<el-table-column
prop="goods_source_name"
:label="t('goodsSourceName')"
min-width="130"
/>
<el-table-column
prop="rule_type_name"
:label="t('ruleTypeName')"
min-width="130"
/>
<el-table-column
prop="rank_type_name"
:label="t('rankTypeName')"
min-width="130"
/>
<el-table-column prop="show_goods_num" :label="t('showGoodsNum')" min-width="130" />
<el-table-column prop="goods_source_name" :label="t('goodsSourceName')" min-width="130" />
<el-table-column prop="rule_type_name" :label="t('ruleTypeName')" min-width="130" />
<el-table-column prop="rank_type_name" :label="t('rankTypeName')" min-width="130" />
</el-table>
<div class="mt-[16px] flex">
<div class="flex items-center flex-1">
<div
class="layui-table-bottom-left-container mr-[10px]"
v-show="selectRankNum"
>
<div class="layui-table-bottom-left-container mr-[10px]" v-show="selectRankNum">
<span>{{ t('goodsSelectPopupBeforeTip') }}</span>
<span class="text-primary mx-[2px]">{{ selectRankNum }}</span>
<span>{{ t('rankSelectPopupAfterTip') }}</span>
</div>
<el-button
type="primary"
link
@click="clear"
v-show="selectRankNum"
>{{ t('goodsSelectPopupClearGoods') }}</el-button
>
<el-button type="primary" link @click="clear" v-show="selectRankNum">{{ t('goodsSelectPopupClearGoods') }}</el-button>
</div>
<el-pagination
v-model:current-page="rankTable.page"
v-model:page-size="rankTable.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="rankTable.total"
@size-change="loadRankList()"
@current-change="loadRankList"
/>
<el-pagination v-model:current-page="rankTable.page" v-model:page-size="rankTable.limit" layout="total, sizes, prev, pager, next, jumper" :total="rankTable.total" @size-change="loadRankList()" @current-change="loadRankList" />
</div>
<template #footer>
@ -129,28 +66,28 @@ import { getSelectRankPageList } from '@/addon/shop/api/marketing'
const prop = defineProps({
modelValue: {
type: String,
default: '',
default: ''
},
max: {
type: Number,
default: 0,
default: 0
},
min: {
type: Number,
default: 0,
},
})
default: 0
}
});
const emit = defineEmits(['update:modelValue', 'rankSelect'])
const emit = defineEmits(['update:modelValue', 'rankSelect']);
const rankIds = computed({
get() {
return prop.modelValue
return prop.modelValue;
},
set(value) {
emit('update:modelValue', value)
},
})
emit('update:modelValue', value);
}
});
const showDialog = ref(false)
@ -171,7 +108,7 @@ const rankTable = reactive({
searchParam: {
name: '',
verify_rank_ids:'',
},
}
})
const searchFormRef = ref()
@ -185,24 +122,24 @@ const multipleSelection: any = ref([])
const handleSelectChange = (selection: any, row: any) => {
if (prop.max === 1) {
//
rankListTableRef.value.clearSelection() //
rankListTableRef.value.toggleRowSelection(row, true)
rankListTableRef.value.clearSelection(); //
rankListTableRef.value.toggleRowSelection(row, true);
for (const key in selectRank) {
delete selectRank[key]
delete selectRank[key];
}
selectRank['rank_' + row.rank_id] = row
selectRank['rank_' + row.rank_id] = row;
} else {
let isSelected = false
let isSelected = false;
for (let i = 0; i < selection.length; i++) {
if (selection[i].rank_id === row.rank_id) {
isSelected = true
break
isSelected = true;
break;
}
}
if (isSelected) {
selectRank['rank_' + row.rank_id] = row
selectRank['rank_' + row.rank_id] = row;
} else {
delete selectRank['rank_' + row.rank_id]
delete selectRank['rank_' + row.rank_id];
}
}
}
@ -210,19 +147,19 @@ const handleSelectChange = (selection: any, row: any) => {
//
const handleSelectAllChange = (selection: any) => {
if (prop.max == 1) {
rankListTableRef.value.clearSelection()
rankListTableRef.value.clearSelection();
for (const key in selectRank) {
delete selectRank[key]
delete selectRank[key];
}
} else {
if (selection.length) {
selection.forEach((item: any) => {
selectRank['rank_' + item.rank_id] = item
})
selectRank['rank_' + item.rank_id] = item;
});
} else {
rankTable.data.forEach((item: any) => {
delete selectRank['rank_' + item.rank_id]
})
delete selectRank['rank_' + item.rank_id];
});
}
}
}
@ -230,14 +167,14 @@ const handleSelectAllChange = (selection: any) => {
//
const setRankSelected = () => {
nextTick(() => {
if (!rankListTableRef.value) return
if (!rankListTableRef.value) return;
for (let i = 0; i < rankTable.data.length; i++) {
rankListTableRef.value.toggleRowSelection(rankTable.data[i], false)
rankListTableRef.value.toggleRowSelection(rankTable.data[i], false);
if (selectRank['rank_' + rankTable.data[i].rank_id]) {
rankListTableRef.value.toggleRowSelection(rankTable.data[i], true)
rankListTableRef.value.toggleRowSelection(rankTable.data[i], true);
}
}
})
});
}
/**
@ -247,37 +184,36 @@ const loadRankList = (page: number = 1, callback: any = null) => {
rankTable.loading = true
rankTable.page = page
const searchData: any = cloneDeep(rankTable.searchParam)
const searchData: any = cloneDeep(rankTable.searchParam);
getSelectRankPageList({
page: rankTable.page,
limit: rankTable.limit,
...searchData,
})
.then((res) => {
...searchData
}).then(res => {
rankTable.loading = false
rankTable.data = res.data.data
rankTable.total = res.data.total
if (callback) callback(res.data.verify_rank_ids)
setRankSelected()
})
.catch(() => {
setRankSelected();
}).catch(() => {
rankTable.loading = false
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
rankTable.searchParam.verify_rank_ids = ''
rankTable.searchParam.name = ''
rankTable.searchParam.verify_rank_ids = '';
rankTable.searchParam.name = '';
loadRankList()
}
const show = () => {
// idid
rankTable.searchParam.verify_rank_ids = rankIds.value
rankTable.searchParam.verify_rank_ids = rankIds.value;
loadRankList(1, (verify_rank_ids: any) => {
//
@ -285,13 +221,13 @@ const show = () => {
rankIds.value.splice(0, rankIds.value.length, ...verify_rank_ids)
rankIds.value.forEach((item: any) => {
if (!selectRank['rank_' + item]) {
selectRank['rank_' + item] = {}
selectRank['rank_' + item] = {};
}
})
//
for (let i = 0; i < rankTable.data.length; i++) {
if (rankIds.value.indexOf(rankTable.data[i].rank_id) != -1) {
selectRank['rank_' + rankTable.data[i].rank_id] = rankTable.data[i]
selectRank['rank_' + rankTable.data[i].rank_id] = rankTable.data[i];
}
}
}
@ -302,33 +238,28 @@ const show = () => {
//
const clear = () => {
for (let k in selectRank) {
delete selectRank[k]
delete selectRank[k];
}
setRankSelected()
setRankSelected();
}
const save = () => {
if (prop.min && selectRankNum.value < prop.min) {
ElMessage({
type: 'warning',
message: `${t('rankSelectPopupGoodsMinTip')}${prop.min}${t('goodsSelectPopupPiece')}`,
})
return
});
return;
}
if (
prop.max &&
prop.max > 0 &&
selectRankNum.value &&
selectRankNum.value > prop.max
) {
if (prop.max && prop.max > 0 && selectRankNum.value && selectRankNum.value > prop.max) {
ElMessage({
type: 'warning',
message: `${t('rankSelectPopupGoodsMaxTip')}${prop.max}${t('goodsSelectPopupPiece')}`,
})
return
});
return;
}
let ids: any = []
let ids: any = [];
for (let k in selectRank) {
ids.push(parseInt(k.replace('rank_', '')))
ids.push(parseInt(k.replace('rank_', '')));
}
rankIds.value.splice(0, rankIds.value.length, ...ids)
emit('rankSelect',selectRank)
@ -338,7 +269,7 @@ const save = () => {
defineExpose({
showDialog,
selectRank,
selectRankNum,
selectRankNum
})
</script>
@ -352,3 +283,4 @@ defineExpose({
}
}
</style>

108
admin/src/addon/shop/views/marketing/goods_rank/config.vue

@ -6,24 +6,14 @@
<span class="text-page-title">{{ pageName }}</span>
</div>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
>
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form">
<el-card class="box-card !border-none" shadow="never">
<!-- <el-form-item :label="t('rankName')" prop="rank_name">
<div class="flex items-center mx-[5px]">
<el-input v-model.trim="formData.rank_name" clearable class="input-width" :placeholder="t('rankNamePlaceholder')" maxlength="20" show-word-limit />
</div>
</el-form-item> -->
<el-form-item
:label="t('rankImages')"
prop="rank_images"
:rules="[
{
<el-form-item :label="t('rankImages')" prop="rank_images" :rules="[{
required: true,
trigger: 'change',
validator: (rule: any, value: any, callback: any) => {
@ -31,10 +21,8 @@
callback(t('imagePlaceholder'))
}
callback()
},
},
]"
>
}
}]">
<upload-image v-model="formData.rank_images" :limit="1" />
</el-form-item>
<el-form-item :label="t('noColor')">
@ -44,19 +32,9 @@
<el-color-picker v-model="formData.select_color" show-alpha />
</el-form-item>
<el-form-item :label="t('selectedBgColor')">
<el-color-picker
v-model="formData.select_bg_color_start"
show-alpha
/>
<icon
name="iconfont iconmap-connect"
size="20px"
class="block !text-gray-400 mx-[5px]"
/>
<el-color-picker
v-model="formData.select_bg_color_end"
show-alpha
/>
<el-color-picker v-model="formData.select_bg_color_start" show-alpha />
<icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
<el-color-picker v-model="formData.select_bg_color_end" show-alpha />
</el-form-item>
<!-- <el-form-item :label="t('rankRemark')" prop="rank_remark">
@ -70,33 +48,33 @@
<!-- 提交按钮 -->
<div class="fixed-footer-wrap">
<div class="fixed-footer h-[48px]">
<el-button type="primary" @click="onSave">{{ t('save') }}</el-button>
<el-button type="primary" @click="onSave">{{ t("save") }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
import { t } from '@/lang'
import { useRoute } from 'vue-router'
import { FormInstance } from 'element-plus'
import { getRankConfig, setRankConfig } from '@/addon/shop/api/marketing'
import { ref, computed } from "vue";
import { t } from "@/lang";
import { useRoute } from "vue-router";
import { FormInstance } from "element-plus";
import { getRankConfig, setRankConfig } from "@/addon/shop/api/marketing";
const route = useRoute()
const pageName = route.meta.title
const route = useRoute();
const pageName = route.meta.title;
const formData = ref({
// rank_name: "",
// rank_remark: "",
no_color: '#FFFCF5',
select_color: '#FF4142',
select_bg_color_start: '#FFFFFF',
select_bg_color_end: '#FFEBD7',
rank_images: '',
})
no_color: "#FFFCF5",
select_color: "#FF4142",
select_bg_color_start: "#FFFFFF",
select_bg_color_end: "#FFEBD7",
rank_images: "",
});
const formRef = ref<FormInstance>()
const formRef = ref<FormInstance>();
const formRules = computed(() => {
return {
@ -106,22 +84,22 @@ const formRules = computed(() => {
rank_images: [
{
required: true,
message: t('imagePlaceholder'),
trigger: 'change',
message: t("imagePlaceholder"),
trigger: "change",
},
],
}
})
};
});
const getRankConfigFn = () => {
getRankConfig().then((res: any) => {
Object.keys(formData.value).forEach((key) => {
if (res.data[key]) formData.value[key] = res.data[key]
})
if (res.data[key]) formData.value[key] = res.data[key];
});
})
}
};
getRankConfigFn()
getRankConfigFn();
/**
* 使用默认说明
@ -131,25 +109,23 @@ getRankConfigFn()
// }
/**** 提交 ****/
const preventDuplication = ref(false)
const preventDuplication = ref(false);
const onSave = async () => {
if (preventDuplication.value) return
if (preventDuplication.value) return;
await formRef.value?.validate(async(valid) => {
if (valid) {
preventDuplication.value = true
console.log(formData.value)
preventDuplication.value = true;
console.log(formData.value);
setRankConfig(formData.value)
.then(() => {
getRankConfigFn()
preventDuplication.value = false
})
.catch(() => {
preventDuplication.value = false
})
}
})
setRankConfig(formData.value).then(() => {
getRankConfigFn();
preventDuplication.value = false;
}).catch(() => {
preventDuplication.value = false;
});
}
});
};
</script>
<style lang="scss" scoped></style>

568
admin/src/addon/shop/views/marketing/goods_rank/edit.vue

@ -1,148 +1,61 @@
<template>
<div class="main-container">
<el-card class="box-card !border-none" shadow="never">
<el-page-header
:content="rank_id ? t('updateRanking') : t('addRanking')"
:icon="ArrowLeft"
@back="back()"
/>
<el-page-header :content="rank_id ? t('updateRanking') : t('addRanking')" :icon="ArrowLeft" @back="back()" />
</el-card>
<el-card
class="box-card mt-[15px] !border-none"
shadow="never"
v-loading="loading"
>
<el-form
:model="formData"
label-width="120px"
ref="formRef"
:rules="formRules"
class="page-form"
>
<el-card class="box-card mt-[15px] !border-none" shadow="never" v-loading="loading">
<el-form :model="formData" label-width="120px" ref="formRef" :rules="formRules" class="page-form">
<el-form-item :label="t('rankName')" prop="name">
<el-input
v-model.trim="formData.name"
clearable
:placeholder="t('rankNamePlaceholder')"
class="input-width"
maxlength="10"
show-word-limit
/>
<el-input v-model.trim="formData.name" clearable :placeholder="t('rankNamePlaceholder')" class="input-width" maxlength="10" show-word-limit />
</el-form-item>
<el-form-item :label="t('rankType')" prop="rank_type">
<el-select
v-model="formData.rank_type"
:placeholder="t('rankTypePlaceholder')"
clearable
>
<el-option
v-for="(value, key) in formList.rankTypeList"
:key="key"
:label="value"
:value="key"
/>
<el-select v-model="formData.rank_type" :placeholder="t('rankTypePlaceholder')" clearable>
<el-option v-for="(value, key) in formList.rankTypeList" :key="key" :label="value" :value="key" />
</el-select>
</el-form-item>
<div
class="ml-[120px] mb-[10px] text-[12px] text-[#999] leading-[20px]"
>
{{ t('rankTypeTips') }}
</div>
<div class="ml-[120px] mb-[10px] text-[12px] text-[#999] leading-[20px]">{{ t('rankTypeTips') }}</div>
<el-form-item :label="t('ruleType')" prop="rule_type">
<el-radio-group v-model="formData.rule_type">
<el-radio
v-for="(value, key) in formList.ruleTypeList"
:key="key"
:label="key"
>{{ value }}</el-radio
>
<el-radio v-for="(value, key) in formList.ruleTypeList" :key="key" :label="key">{{ value }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="t('goodsSource')" prop="goods_source">
<el-radio-group v-model="formData.goods_source">
<el-radio
v-for="(value, key) in formList.goodsSourceList"
:key="key"
:label="key"
>{{ value }}</el-radio
>
<el-radio v-for="(value, key) in formList.goodsSourceList" :key="key" :label="key">{{ value }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="t('selectGoods')"
prop="goods_json"
v-if="formData.goods_source == 'goods'"
>
<goods-select-popup
ref="goodsSelectPopupRef"
v-model="formData.goods_ids"
@goodsSelect="goodsSelect"
:min="1"
:max="99"
/>
<el-form-item :label="t('selectGoods')" prop="goods_json" v-if="formData.goods_source == 'goods'">
<goods-select-popup ref="goodsSelectPopupRef" v-model="formData.goods_ids" @goodsSelect="goodsSelect" :min="1" :max="99" />
</el-form-item>
<el-form-item
v-if="
goods_json && goods_json.length && formData.goods_source == 'goods'
"
>
<el-form-item v-if="goods_json && goods_json.length && formData.goods_source == 'goods' ">
<el-table :data="goods_json" size="large" max-height="400">
<el-table-column
prop="goods_id"
:label="t('goodsSelectPopupGoodsInfo')"
min-width="300"
>
<el-table-column prop="goods_id" :label="t('goodsSelectPopupGoodsInfo')" min-width="300">
<template #default="{ row }">
<div class="flex items-center cursor-pointer">
<div
class="min-w-[60px] h-[60px] flex items-center justify-center"
>
<el-image
v-if="row.goods_image"
class="w-[60px] h-[60px]"
:src="img(row.goods_image)"
fit="contain"
>
<div class="min-w-[60px] h-[60px] flex items-center justify-center">
<el-image v-if="row.goods_image" class="w-[60px] h-[60px]" :src="img(row.goods_image)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[60px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
/>
<img class="w-[60px] h-[60px]" src="@/addon/shop/assets/goods_default.png" />
</div>
</template>
</el-image>
<img
v-else
class="w-[70px] h-[60px]"
src="@/addon/shop/assets/goods_default.png"
fit="contain"
/>
<img v-else class="w-[70px] h-[60px]" src="@/addon/shop/assets/goods_default.png" fit="contain" />
</div>
<div class="ml-2">
<span :title="row.sku_name" class="multi-hidden">{{
row.sku_name
? row.goods_name + ' ' + row.sku_name
: row.goods_name
}}</span>
<span class="text-primary text-[12px]">{{
row.goods_type_name
}}</span>
<span :title="row.sku_name" class="multi-hidden">{{row.sku_name ? row.goods_name + " " + row.sku_name: row.goods_name}}</span>
<span class="text-primary text-[12px]">{{row.goods_type_name}}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column
prop="price"
:label="t('goodsSelectPopupPrice')"
min-width="120"
>
<el-table-column prop="price" :label="t('goodsSelectPopupPrice')" min-width="120">
<template #default="{ row }">
<div>{{ row.price }}</div>
</template>
@ -152,12 +65,7 @@
<template #header>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">{{ t('sort') }}</span>
<el-tooltip
class="box-item"
effect="light"
:content="t('sortRules')"
placement="top"
>
<el-tooltip class="box-item" effect="light" :content="t('sortRules')" placement="top">
<el-icon color="#666">
<QuestionFilled />
</el-icon>
@ -165,269 +73,174 @@
</div>
</template>
<template #default="{ row }">
<el-input
@keyup="filterNumber($event)"
v-model.number="row.sort"
class="w-[70px]"
maxlength="8"
/>
<el-input @keyup="filterNumber($event)" v-model.number ="row.sort" class="w-[70px]" maxlength="8" />
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
align="right"
min-width="160"
>
<el-table-column :label="t('operation')" align="right" min-width="160">
<template #default="{ row, $index }">
<el-button
type="primary"
link
@click="deleteGoodsEvent(row, $index)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="deleteGoodsEvent(row, $index)">{{ t("delete") }}</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item
:label="t('goodsSelectPopupGoodsCategory')"
prop="category_ids"
v-if="formData.goods_source == 'category'"
>
<el-cascader
v-model="formData.category_ids"
:options="goodsCategoryOptions"
:props="goodsCategoryProps"
clearable
filterable
@change="categoryHandleChange"
popper-class="choice"
/>
<el-form-item :label="t('goodsSelectPopupGoodsCategory')" prop="category_ids" v-if="formData.goods_source == 'category'">
<el-cascader v-model="formData.category_ids" :options="goodsCategoryOptions" :props="goodsCategoryProps" clearable filterable @change="categoryHandleChange" popper-class="choice" />
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshGoodsCategory(true)"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toGoodsCategoryEvent"
>{{ t('addCategory') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshGoodsCategory(true)">{{ t("refresh")}}</span>
<span class="cursor-pointer text-primary" @click="toGoodsCategoryEvent">{{ t("addCategory") }}</span>
</div>
</el-form-item>
<el-form-item
:label="t('brand')"
prop="brand_ids"
v-if="formData.goods_source == 'brand'"
>
<el-select
v-model="formData.brand_ids"
:placeholder="t('brandPlaceholder')"
clearable
multiple
>
<el-option
v-for="item in brandOptions"
:key="item.brand_id"
:label="item.brand_name"
:value="item.brand_id"
/>
<el-form-item :label="t('brand')" prop="brand_ids" v-if="formData.goods_source == 'brand'">
<el-select v-model="formData.brand_ids" :placeholder="t('brandPlaceholder')" clearable multiple>
<el-option v-for="item in brandOptions" :key="item.brand_id" :label="item.brand_name" :value="item.brand_id" />
</el-select>
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshGoodsBrand(true)"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toGoodsBrandEvent"
>{{ t('addBrand') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshGoodsBrand(true)">{{ t("refresh")}}</span>
<span class="cursor-pointer text-primary" @click="toGoodsBrandEvent">{{ t("addBrand") }}</span>
</div>
</el-form-item>
<el-form-item
:label="t('label')"
prop="label_ids"
v-if="formData.goods_source == 'label'"
>
<el-form-item :label="t('label')" prop="label_ids" v-if="formData.goods_source == 'label'">
<el-checkbox-group v-model="formData.label_ids">
<el-checkbox
:label="item.label_id"
v-for="(item, index) in labelOptions"
:key="index"
>{{ item.label_name }}</el-checkbox
>
<el-checkbox :label="item.label_id" v-for="(item, index) in labelOptions" :key="index">{{ item.label_name}}</el-checkbox>
</el-checkbox-group>
<div class="ml-[10px]">
<span
class="cursor-pointer text-primary mr-[10px]"
@click="refreshGoodsLabel"
>{{ t('refresh') }}</span
>
<span
class="cursor-pointer text-primary"
@click="toGoodsLabelEvent"
>{{ t('addLabel') }}</span
>
<span class="cursor-pointer text-primary mr-[10px]" @click="refreshGoodsLabel">{{ t("refresh") }}</span>
<span class="cursor-pointer text-primary" @click="toGoodsLabelEvent">{{ t("addLabel") }}</span>
</div>
</el-form-item>
<el-form-item :label="t('sort')" prop="sort">
<el-input
v-model.number="formData.sort"
clearable
:placeholder="t('sortPlaceholder')"
class="input-width"
maxlength="8"
show-word-limit
@keyup="filterNumber($event)"
@blur="formData.sort = $event.target.value"
/>
<el-input v-model.number="formData.sort" clearable :placeholder="t('sortPlaceholder')" class="input-width" maxlength="8" show-word-limit @keyup="filterNumber($event)" @blur="formData.sort = $event.target.value" />
</el-form-item>
<el-form-item :label="t('isShow')" prop="status">
<el-switch
v-model="formData.status"
:active-value="1"
:inactive-value="0"
/>
<el-switch v-model="formData.status" :active-value="1" :inactive-value="0" />
</el-form-item>
</el-form>
</el-card>
<div class="fixed-footer-wrap">
<div class="fixed-footer">
<el-button type="primary" @click="save()">{{ t('save') }}</el-button>
<el-button @click="back()">{{ t('cancel') }}</el-button>
<el-button type="primary" @click="save()">{{ t("save") }}</el-button>
<el-button @click="back()">{{ t("cancel") }}</el-button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, reactive } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import { ArrowLeft } from '@element-plus/icons-vue'
import { FormInstance, ElMessage } from 'element-plus'
import {
optionData,
getRankInfo,
addGoodRank,
editGoodRank,
} from '@/addon/shop/api/marketing'
import goodsSelectPopup from '@/addon/shop/views/goods/components/goods-select-popup.vue'
import {
getBrandList,
getLabelList,
getCategoryTree,
} from '@/addon/shop/api/goods'
import { ref, computed, reactive } from "vue";
import { t } from "@/lang";
import { useRoute, useRouter } from "vue-router";
import { ArrowLeft } from "@element-plus/icons-vue";
import { FormInstance, ElMessage } from "element-plus";
import {optionData,getRankInfo,addGoodRank,editGoodRank,} from "@/addon/shop/api/marketing";
import goodsSelectPopup from "@/addon/shop/views/goods/components/goods-select-popup.vue";
import {getBrandList,getLabelList,getCategoryTree} from "@/addon/shop/api/goods";
import {img, deepClone ,filterNumber} from '@/utils/common'
const router = useRouter()
const route = useRoute()
const loading = ref(true)
const rank_id = route.query.rank_id
const goods_json = ref([])
const router = useRouter();
const route = useRoute();
const loading = ref(true);
const rank_id = route.query.rank_id;
const goods_json = ref([]);
const formData = reactive({
name: '',
goods_source: 'all',
rank_type: '',
sort: '',
rule_type: 'sale',
name: "",
goods_source: "all",
rank_type: "",
sort: "",
rule_type: "sale",
category_ids: [],
status: 1,
brand_ids: [],
label_ids: [],
goods_json: <Array<any>>[],
goods_ids: [],
})
});
const formList = reactive({
rankTypeList: [],
goodsSourceList: [],
ruleTypeList: [],
})
const formRef = ref<FormInstance>()
});
const formRef = ref<FormInstance>();
const formRules = computed(() => {
return {
name: [
{ required: true, message: t('rankNamePlaceholder'), trigger: 'blur' },
{ required: true, message: t("rankNamePlaceholder"), trigger: "blur" },
],
rule_type: [
{
required: true,
message: t('ruleTypePlaceholder'),
trigger: 'change',
message: t("ruleTypePlaceholder"),
trigger: "change",
},
],
rank_type: [
{
required: true,
message: t('rankTypePlaceholder'),
trigger: 'change',
message: t("rankTypePlaceholder"),
trigger: "change",
},
],
goods_source: [
{
required: true,
message: t('goodsSourcePlaceholder'),
trigger: 'change',
message: t("goodsSourcePlaceholder"),
trigger: "change",
},
],
label_ids: [
{
validator: (rule: any, value: any, callback: any) => {
if (formData.goods_source === 'label') {
if (formData.goods_source === "label") {
if (!value || value.length === 0) {
callback(t('labelTips')) //
callback(t("labelTips")); //
} else {
callback() //
callback(); //
}
} else {
callback()
callback();
}
},
trigger: 'change',
trigger: "change",
},
],
brand_ids: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (formData.goods_source === 'brand') {
if (formData.goods_source === "brand") {
if (!value || value.length === 0) {
callback(t('brandTips')) //
callback(t("brandTips")); //
} else {
callback() //
callback(); //
}
} else {
callback()
callback();
}
},
trigger: 'change',
trigger: "change",
},
],
category_ids: [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (formData.goods_source === 'category') {
if (formData.goods_source === "category") {
if (!value || value.length === 0) {
callback(t('categoryTips')) //
callback(t("categoryTips")); //
} else {
callback() //
callback(); //
}
} else {
callback()
callback();
}
},
trigger: 'change',
trigger: "change",
},
],
goods_json: [
@ -442,32 +255,33 @@ const formRules = computed(() => {
callback()
}
}
},
},
],
}
})
}
],
};
});
const getRankDetails= () => {
optionData().then((res) => {
loading.value = false
formList.rankTypeList = res.data.rank_type
formList.goodsSourceList = res.data.goods_source
formList.ruleTypeList = res.data.rule_type
})
loading.value = false;
formList.rankTypeList = res.data.rank_type;
formList.goodsSourceList = res.data.goods_source;
formList.ruleTypeList = res.data.rule_type;
});
if (rank_id) {
//
loading.value = true
const id = Number(rank_id)
loading.value = true;
const id = Number(rank_id);
getRankInfo(id).then((res) => {
const data = res.data
const data = res.data;
if (data) {
Object.assign(formData, data)
Object.assign(formData, data);
if (formData.goods_source == 'goods') {
formData.goods_ids.splice(0, formData.goods_ids.length)
formData.goods_ids.splice(0, formData.goods_ids.length);
formData.goods_json.forEach((item: any) => {
formData.goods_ids.push( item.goods_id)
})
}
goods_json.value = data.goods_list.map((item: any) => {
return {
@ -477,142 +291,143 @@ const getRankDetails = () => {
goods_type_name: item.goods_type_name,
goods_name: item.goods_name,
goods_image: item.goodsSku.sku_image,
};
});
loading.value = false;
}
})
loading.value = false
}
})
}
}
getRankDetails()
};
getRankDetails();
//
const goodsCategoryOptions = reactive([])
const goodsCategoryOptions = reactive([]);
const goodsCategoryProps = {
multiple: true,
}
};
const categoryHandleChange = (value: any) => {
console.log(value, formData.category_ids, formData.category_ids.toString())
}
console.log(value, formData.category_ids, formData.category_ids.toString());
};
//
const toGoodsCategoryEvent = () => {
const url = router.resolve({
path: '/shop/goods/category',
})
window.open(url.href)
}
path: "/shop/goods/category",
});
window.open(url.href);
};
//
const refreshGoodsCategory = (bool = false) => {
getCategoryTree().then((res) => {
const data = res.data
const data = res.data;
if (data) {
const goodsCategoryTree: any = []
const goodsCategoryTree: any = [];
data.forEach((item: any) => {
const children: any = []
const children: any = [];
if (item.child_list) {
item.child_list.forEach((childItem: any) => {
children.push({
value: childItem.category_id,
label: childItem.category_name,
})
})
});
});
}
goodsCategoryTree.push({
value: item.category_id,
label: item.category_name,
children,
})
})
});
});
goodsCategoryOptions.splice(
0,
goodsCategoryOptions.length,
...goodsCategoryTree
)
);
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
})
message: t("refreshSuccess"),
type: "success",
});
}
}
})
}
});
};
refreshGoodsCategory()
refreshGoodsCategory();
//
const brandOptions = reactive([])
const brandOptions = reactive([]);
//
const toGoodsBrandEvent = () => {
const url = router.resolve({
path: '/shop/goods/brand',
})
window.open(url.href)
}
path: "/shop/goods/brand",
});
window.open(url.href);
};
//
const refreshGoodsBrand = (bool = false) => {
getBrandList({}).then((res) => {
const data = res.data
const data = res.data;
if (data) {
brandOptions.splice(0, brandOptions.length, ...data)
brandOptions.splice(0, brandOptions.length, ...data);
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
})
}
message: t("refreshSuccess"),
type: "success",
});
}
})
}
});
};
refreshGoodsBrand()
refreshGoodsBrand();
//
const labelOptions = reactive([])
const labelOptions = reactive([]);
//
const toGoodsLabelEvent = () => {
const url = router.resolve({
path: '/shop/goods/label',
})
window.open(url.href)
}
path: "/shop/goods/label",
});
window.open(url.href);
};
//
const refreshGoodsLabel = (bool = false) => {
getLabelList({}).then((res) => {
const data = res.data
const data = res.data;
if (data) {
labelOptions.splice(0, labelOptions.length, ...data)
labelOptions.splice(0, labelOptions.length, ...data);
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success',
})
message: t("refreshSuccess"),
type: "success",
});
}
}
})
}
});
};
refreshGoodsLabel()
refreshGoodsLabel();
//
const goodsSelect = (value: any) => {
let arr = []
let arr = [];
for (let key in value) {
let goods_sku: any = value[key]
let goods_sku: any = value[key];
let sku: any = {
goods_id: goods_sku.goods_id,
price: goods_sku.goodsSku.price,
goods_type_name: goods_sku.goods_type_name,
goods_image: goods_sku.goods_cover,
goods_name: goods_sku.goods_name,
}
};
if (goods_json.value.length) {
goods_json.value.forEach((el: any) => {
if (el.goods_id == sku.goods_id) {
@ -622,19 +437,19 @@ const goodsSelect = (value: any) => {
}
arr.push(deepClone(sku))
}
goods_json.value = arr
}
goods_json.value = arr;
};
//
const deleteGoodsEvent = (row: any, index: any) => {
goods_json.value.splice(index, 1)
formData.goods_ids.splice(formData.goods_ids.indexOf(row.goods_id), 1)
}
goods_json.value.splice(index, 1);
formData.goods_ids.splice(formData.goods_ids.indexOf(row.goods_id), 1);
};
const preventDuplication = ref(false)
const preventDuplication = ref(false);
const save = async () => {
//
if (preventDuplication.value) return
if (preventDuplication.value) return;
await formRef.value?.validate(async (valid) => {
if (valid) {
@ -643,57 +458,54 @@ const save = async () => {
return {
goods_id: item.goods_id, // goods_id
sort: item.sort, // sort
}
})
};
});
const goodsCategory:any = []
formData.category_ids.forEach((item: any) => {
if (Array.isArray(item) && item.length === 2) {
goodsCategory.push(item[1])
goodsCategory.push(item[1]);
} else if(Array.isArray(item) && item.length === 1) {
goodsCategory.push(item[0])
goodsCategory.push(item[0]);
}else{
goodsCategory.push(item)
goodsCategory.push(item);
}
})
});
formData.category_ids = goodsCategory
if (rank_id) {
formData.id = rank_id // rank_id dataToSubmit
editGoodRank(formData)
.then((res) => {
formData.id = rank_id; // rank_id dataToSubmit
editGoodRank(formData).then((res) => {
loading.value = false
preventDuplication.value = false
preventDuplication.value = false;
if (res.data) {
router.push('/shop/marketing/goods_rank/list')
router.push("/shop/marketing/goods_rank/list");
}
})
.catch(() => {
loading.value = false
preventDuplication.value = false
})
}).catch(() => {
loading.value = false;
preventDuplication.value = false;
});
} else {
addGoodRank(formData)
.then((res) => {
addGoodRank(formData).then((res) => {
loading.value = false
preventDuplication.value = false
preventDuplication.value = false;
if (res.data) {
router.push('/shop/marketing/goods_rank/list')
router.push("/shop/marketing/goods_rank/list");
}
})
.catch(() => {
loading.value = false
preventDuplication.value = false
})
}).catch(() => {
loading.value = false;
preventDuplication.value = false;
});
}
} else {
preventDuplication.value = false
}
})
preventDuplication.value = false;
}
});
};
const back = () => {
router.push('/shop/marketing/goods_rank/list')
}
};
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

327
admin/src/addon/shop/views/marketing/goods_rank/list.vue

@ -3,34 +3,18 @@
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="handleChange">{{
t('addRanking')
}}</el-button>
<el-button type="primary" @click="handleChange">{{ t("addRanking") }}</el-button>
</div>
<!-- 搜索 -->
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="tableData.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('rankName')" prop="rankName">
<el-input
v-model.trim="tableData.searchParam.name"
:placeholder="t('rankNamePlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.name" :placeholder="t('rankNamePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadRankList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadRankList()">{{ t("search") }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t("reset") }}</el-button>
</el-form-item>
</el-form>
</el-card>
@ -38,75 +22,29 @@
<!-- 列表 -->
<div>
<div class="mb-[10px] flex items-center">
<el-checkbox
v-model="toggleCheckbox"
size="large"
class="px-[14px]"
@change="toggleChange"
:indeterminate="isIndeterminate"
/>
<el-button @click="batchDeleteGoods" size="small">{{
t('batchDeletion')
}}</el-button>
<el-checkbox v-model="toggleCheckbox" size="large" class="px-[14px]" @change="toggleChange" :indeterminate="isIndeterminate" />
<el-button @click="batchDeleteGoods" size="small">{{t("batchDeletion")}}</el-button>
</div>
<el-table
:data="tableData.data"
size="large"
v-loading="tableData.loading"
ref="goodBankListTableRef"
@sort-change="sortChange"
@selection-change="handleSelectionChange"
>
<el-table :data="tableData.data" size="large" v-loading="tableData.loading" ref="goodBankListTableRef" @sort-change="sortChange" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
<span>{{ !tableData.loading ? t("emptyData") : "" }}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" :label="t('rankName')" min-width="130" />
<el-table-column
prop="show_goods_num"
:label="t('showGoodsNum')"
min-width="130"
/>
<el-table-column
prop="goods_source_name"
:label="t('goodsSource')"
min-width="130"
/>
<el-table-column
prop="rule_type_name"
:label="t('ruleType')"
min-width="130"
/>
<el-table-column
prop="rank_type_name"
:label="t('rankType')"
min-width="130"
/>
<el-table-column prop="show_goods_num" :label="t('showGoodsNum')" min-width="130" />
<el-table-column prop="goods_source_name" :label="t('goodsSource')" min-width="130" />
<el-table-column prop="rule_type_name" :label="t('ruleType')" min-width="130" />
<el-table-column prop="rank_type_name" :label="t('rankType')" min-width="130" />
<el-table-column prop="status" :label="t('isShow')" width="130">
<template #default="{ row }">
<el-tag
class="cursor-pointer"
:type="row.status != 0 ? 'success' : 'danger'"
@click="showClick(row)"
>{{ row.status != 0 ? '开启' : '关闭' }}</el-tag
>
<el-tag class="cursor-pointer" :type="row.status != 0 ? 'success' : 'danger'" @click="showClick(row)">{{ row.status != 0 ? '开启' : '关闭' }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="sort"
min-width="120"
:show-overflow-tooltip="true"
sortable="custom"
>
<el-table-column prop="sort" min-width="120" :show-overflow-tooltip="true" sortable="custom">
<template #header>
<div style="display: inline-flex; align-items: center">
<span class="mr-[5px]">{{ t('sort') }}</span>
<el-tooltip
class="box-item"
effect="light"
:content="t('sortRules')"
placement="top"
>
<el-tooltip class="box-item" effect="light" :content="t('sortRules')" placement="top">
<el-icon color="#666">
<QuestionFilled />
</el-icon>
@ -114,53 +52,24 @@
</div>
</template>
<template #default="{ row }">
<el-input
v-model.number="row.sort"
class="w-[70px]"
maxlength="8"
@blur="sortInputListener(row.sort, row)"
/>
<el-input v-model.number="row.sort" class="w-[70px]" maxlength="8" @blur="sortInputListener(row.sort, row)" />
</template>
</el-table-column>
<el-table-column
prop="create_time"
:label="t('createTime')"
min-width="150"
sortable="custom"
>
<el-table-column prop="create_time" :label="t('createTime')" min-width="150" sortable="custom">
<template #default="{ row }">
<div>{{ row.create_time }}</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button type="primary" link @click="editEvent(row)">{{
t('edit')
}}</el-button>
<el-button
type="primary"
link
@click="deleteEvent(row.rank_id)"
>{{ t('delete') }}</el-button
>
<el-button type="primary" link @click="editEvent(row)">{{t("edit")}}</el-button>
<el-button type="primary" link @click="deleteEvent(row.rank_id)">{{ t("delete") }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="loadRankList()"
@current-change="loadRankList"
/>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit" layout="total, sizes, prev, pager, next, jumper" :total="tableData.total" @size-change="loadRankList()" @current-change="loadRankList" />
</div>
</div>
</el-card>
@ -168,29 +77,18 @@
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import {
getRankPageList,
deleteGoodRank,
batchDelete,
modifyGoodsRankSort,
editRankStatus,
} from '@/addon/shop/api/marketing'
import { FormInstance, ElMessage, ElMessageBox } from 'element-plus'
import {
img,
debounce,
setTablePageStorage,
getTablePageStorage,
} from '@/utils/common'
import { ref, reactive } from "vue";
import { t } from "@/lang";
import { useRoute, useRouter } from "vue-router";
import {getRankPageList,deleteGoodRank,batchDelete,modifyGoodsRankSort,editRankStatus} from "@/addon/shop/api/marketing";
import { FormInstance, ElMessage, ElMessageBox } from "element-plus";
import { img, debounce,setTablePageStorage,getTablePageStorage } from "@/utils/common";
const router = useRouter()
const route = useRoute()
const pageName = route.meta.title
const repeat = ref(false)
const searchFormRef = ref<FormInstance>()
const router = useRouter();
const route = useRoute();
const pageName = route.meta.title;
const repeat = ref(false);
const searchFormRef = ref<FormInstance>();
//
const tableData = reactive({
@ -200,54 +98,48 @@ const tableData = reactive({
loading: false,
data: [],
searchParam: {
name: '',
name: "",
order: '',
sort: '',
sort: ''
},
})
});
//
const loadRankList = (page: number = 1) => {
tableData.loading = true
tableData.page = page
tableData.loading = true;
tableData.page = page;
getRankPageList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
})
.then((res) => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
setTablePageStorage(
tableData.page,
tableData.limit,
tableData.searchParam
)
})
.catch(() => {
tableData.loading = false
})
}
}).then((res) => {
tableData.loading = false;
tableData.data = res.data.data;
tableData.total = res.data.total;
setTablePageStorage(tableData.page, tableData.limit, tableData.searchParam);
}).catch(() => {
tableData.loading = false;
});
};
loadRankList(getTablePageStorage(tableData.searchParam).page)
loadRankList(getTablePageStorage(tableData.searchParam).page);
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
tableData.searchParam.name = ''
loadRankList()
}
if (!formEl) return;
formEl.resetFields();
tableData.searchParam.name = "";
loadRankList();
};
//
const handleChange = () => {
router.push('/shop/marketing/goods_rank/edit')
}
router.push("/shop/marketing/goods_rank/edit");
};
const editEvent = (data: any) => {
router.push('/shop/marketing/goods_rank/edit?rank_id=' + data.rank_id)
}
router.push("/shop/marketing/goods_rank/edit?rank_id=" + data.rank_id);
};
const showClick = (row: any) => {
row.status = row.status === 1 ? 0 : 1
@ -260,16 +152,16 @@ const showClick = (row: any) => {
//
const deleteEvent = (id: number) => {
ElMessageBox.confirm(t('deleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
ElMessageBox.confirm(t("deleteTips"), t("warning"), {
confirmButtonText: t("confirm"),
cancelButtonText: t("cancel"),
type: "warning",
}).then(() => {
deleteGoodRank(id).then(() => {
loadRankList()
})
loadRankList();
})
}
});
};
//
const sortChange = (event: any) => {
@ -287,102 +179,101 @@ const sortChange = (event: any) => {
}
//
const toggleCheckbox = ref()
const toggleCheckbox = ref();
//
const isIndeterminate = ref(false)
const isIndeterminate = ref(false);
//
const toggleChange = (value: any) => {
isIndeterminate.value = false
goodBankListTableRef.value.toggleAllSelection()
}
isIndeterminate.value = false;
goodBankListTableRef.value.toggleAllSelection();
};
const goodBankListTableRef = ref()
const goodBankListTableRef = ref();
//
const multipleSelection: any = ref([])
const multipleSelection: any = ref([]);
//
const handleSelectionChange = (val: []) => {
multipleSelection.value = val
multipleSelection.value = val;
toggleCheckbox.value = false
toggleCheckbox.value = false;
if (
multipleSelection.value.length > 0 &&
multipleSelection.value.length < tableData.data.length
) {
isIndeterminate.value = true
isIndeterminate.value = true;
} else {
isIndeterminate.value = false
isIndeterminate.value = false;
}
if (multipleSelection.value.length == tableData.data.length) {
toggleCheckbox.value = true
}
toggleCheckbox.value = true;
}
};
//
const batchDeleteGoods = () => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`,
})
return
type: "warning",
message: `${t("batchEmptySelectedGoodsTips")}`,
});
return;
}
ElMessageBox.confirm(t('batchGoodsDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
ElMessageBox.confirm(t("batchGoodsDeleteTips"), t("warning"), {
confirmButtonText: t("confirm"),
cancelButtonText: t("cancel"),
type: "warning",
}).then(() => {
if (repeat.value) return
repeat.value = true
if (repeat.value) return;
repeat.value = true;
const rankIds: any = []
const rankIds: any = [];
multipleSelection.value.forEach((item: any) => {
rankIds.push(item.rank_id)
})
rankIds.push(item.rank_id);
});
batchDelete({
rank_id: rankIds,
})
.then(() => {
loadRankList()
repeat.value = false
})
.catch(() => {
repeat.value = false
})
})
}
}).then(() => {
loadRankList();
repeat.value = false;
}).catch(() => {
repeat.value = false;
});
});
};
//
const regExp = {
number: /^\d{0,10}$/,
digit: /^\d{0,10}(.?\d{0,2})$/,
}
};
//
const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !regExp.number.test(sort)) {
ElMessage({
type: 'warning',
message: `${t('sortTips')}`,
})
return
type: "warning",
message: `${t("sortTips")}`,
});
return;
}
if (sort > 99999999) {
row.sort = 99999999
row.sort = 99999999;
}
modifyGoodsRankSort({
rank_id: row.rank_id,
sort,
}).then((res) => {
loadRankList()
})
})
loadRankList();
});
});
</script>
<style lang="scss" scoped></style>

34
admin/src/addon/shop/views/marketing/index.vue

@ -1,42 +1,30 @@
<template>
<div class="main-container w-full " v-loading="loading">
<div v-for="(item, index) in detail.appList" :key="index">
<el-card
class="box-card !border-none"
shadow="never"
v-if="item.child != ''"
>
<el-card class="box-card !border-none" shadow="never" v-if="item.child != ''">
<div class="flex justify-between items-center">
<span class="text-page-title">{{item.name}}</span>
</div>
<div class="flex flex-wrap plug-list pb-10 plug-large">
<div v-for="(ite, index) in item.child" :key="index">
<div
class="relative app-item cursor-pointer px-4 mr-4 mt-[20px] bg-[#f7f7f7] border-[1px] hover:border-primary"
>
<div class="relative app-item cursor-pointer px-4 mr-4 mt-[20px] bg-[#f7f7f7] border-[1px] hover:border-primary">
<div @click="toLink(ite.url)" class="flex py-5 items-center">
<div class="flex justify-center items-center">
<el-image
class="w-[40px] h-[40px]"
:src="img(ite.icon)"
fit="contain"
>
<el-image class="w-[40px] h-[40px]" :src="img(ite.icon)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[50px] h-[50px]"
src="@/app/assets/images/index/app_default.png"
/>
<img class="w-[50px] h-[50px]" src="@/app/assets/images/index/app_default.png" />
</div>
</template>
</el-image>
</div>
<div class="flex flex-col justify-between text-left w-[190px]">
<p class="app-text w-[190px] text-[17px] text-[#222] pl-3">
{{ ite.title }}
</p>
<p class="app-text w-[190px] text-[17px] text-[#222] pl-3">{{ ite.title }}</p>
</div>
</div>
</div>
</div>
</div>
@ -66,7 +54,7 @@ interface detailType {
appList: appListType[]
}
const detail = reactive<detailType>({
appList: [],
appList: []
})
const getAppList = async () => {
@ -82,11 +70,11 @@ getAppList()
const toLink = (link: RouteLocationRaw) => {
router.push(link)
}
</script>
<style lang="scss" scoped>
.main-container,
.empty {
.main-container,.empty{
min-height: calc(100vh - 84px);
}
.app-text {

170
admin/src/addon/shop/views/marketing/manjian/detail.vue

@ -1,11 +1,5 @@
<template>
<el-drawer
v-model="showDialog"
:title="t('detailTitle')"
direction="rtl"
:before-close="handleClose"
class="member-detail-drawer"
>
<el-drawer v-model="showDialog" :title="t('detailTitle')" direction="rtl" :before-close="handleClose" class="member-detail-drawer">
<div class="main-container" v-loading="loading">
<el-tabs v-model="activeName" class="pb-[10px]" @tab-change="handleClick">
<el-tab-pane :label="t('basicInfo')" name="basicInfo" />
@ -14,14 +8,7 @@
<div v-if="activeName == 'basicInfo'">
<el-card class="mb-[15px]" >
<h3 class="panel-title">{{ t('basicInfo') }}</h3>
<el-form
class="mt-[15px]"
:model="formData"
label-width="120px"
ref="formRef"
label-position="left"
v-if="Object.keys(formData).length"
>
<el-form class="mt-[15px]" :model="formData" label-width="120px" ref="formRef" label-position="left" v-if="Object.keys(formData).length">
<div class="relative" shadow="never" v-if="formData">
<el-row>
<el-col :span="8">
@ -109,6 +96,7 @@
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
</el-card>
@ -117,21 +105,13 @@
<el-table :data="formData.rule_json" size="large">
<el-table-column :label="t('discountThreshold')" prop="limit">
<template #default="{ row }">
<span v-if="row.limit"
>{{ row.limit
}}{{
formData.condition_type == 'over_n_yuan' ? '元' : '件'
}}</span
>
<span v-if="row.limit">{{ row.limit }}{{ formData.condition_type == 'over_n_yuan' ? '' : '' }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column :label="t('discountMoney')" prop="discount_money">
<template #default="{ row }">
<span v-if="row.discount_money"
>{{ row.discount_money
}}{{ row.discount_type == 1 ? '元' : '折' }}</span
>
<span v-if="row.discount_money">{{ row.discount_money }}{{row.discount_type==1 ? '' : ''}}</span>
<span v-else>-</span>
</template>
</el-table-column>
@ -153,20 +133,10 @@
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column
:label="t('giveCoupon')"
prop="coupon"
min-width="150"
>
<el-table-column :label="t('giveCoupon')" prop="coupon" min-width="150">
<template #default="{ row }">
<div
v-if="row.coupon && row.coupon.length && row.is_give_coupon"
>
<div
v-for="(coupon, index) in row.coupon"
:key="index"
class="flex items-center"
>
<div v-if="row.coupon && row.coupon.length && row.is_give_coupon">
<div v-for="(coupon, index) in row.coupon" :key="index" class="flex items-center">
<div class="goods-name">{{ coupon.title }} </div>
<div>* {{ coupon.num }}</div>
</div>
@ -174,18 +144,10 @@
<div v-else>-</div>
</template>
</el-table-column>
<el-table-column
:label="t('giveGoods')"
prop="goods"
min-width="200"
>
<el-table-column :label="t('giveGoods')" prop="goods" min-width="200">
<template #default="{ row }">
<div v-if="row.goods && row.goods.length &&row.is_give_goods">
<div
v-for="(goods, index) in row.goods"
:key="index"
class="flex items-center"
>
<div v-for="(goods, index) in row.goods" :key="index" class="flex items-center">
<div class="goods-name">{{ goods.goods_name }} </div>
<div>* {{ goods.num }}</div>
</div>
@ -195,90 +157,41 @@
</el-table-column>
</el-table>
</el-card>
</div>
<div v-if="activeName == 'memberList'">
<el-table
:data="memberParams.data"
size="large"
v-loading="memberParams.loading"
>
<el-table :data="memberParams.data" size="large" v-loading="memberParams.loading">
<template #empty>
<span>{{ !memberParams.loading ? t('emptyData') : '' }}</span>
</template>
<el-table-column
prop="goods_id"
:label="t('memberInfo')"
min-width="200"
>
<el-table-column prop="goods_id" :label="t('memberInfo')" min-width="200">
<template #default="{ row }">
<div
class="flex items-center cursor-pointer"
@click="detailEvent(row.member_id)"
>
<div
class="min-w-[50px] h-[50px] flex items-center justify-center"
>
<el-image
v-if="row.headimg"
class="w-[50px] h-[50px]"
:src="img(row.headimg)"
fit="contain"
>
<div class="flex items-center cursor-pointer" @click="detailEvent(row.member_id)">
<div class="min-w-[50px] h-[50px] flex items-center justify-center">
<el-image v-if="row.headimg" class="w-[50px] h-[50px]" :src="img(row.headimg)" fit="contain">
<template #error>
<div class="image-slot">
<img
class="w-[50px] h-[50px] rounded-full"
src="@/app/assets/images/member_head.png"
alt=""
/>
<img class="w-[50px] h-[50px] rounded-full" src="@/app/assets/images/member_head.png" alt="">
</div>
</template>
</el-image>
<img
class="w-[50px] h-[50px] rounded-full"
v-else
src="@/app/assets/images/member_head.png"
alt=""
/>
<img class="w-[50px] h-[50px] rounded-full" v-else src="@/app/assets/images/member_head.png" alt="">
</div>
<div class="ml-2">
<span
:title="row.nickname || row.username"
class="multi-hidden"
>{{ row.nickname || row.username }}</span
>
<span class="text-primary text-[12px]">{{
row.mobile || ''
}}</span>
<span :title="(row.nickname || row.username)" class="multi-hidden">{{row.nickname || row.username}}</span>
<span class="text-primary text-[12px]">{{row.mobile || ''}}</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column
prop="total_order_money"
:label="t('consumptionMoney')"
min-width="100"
/>
<el-table-column
prop="total_num"
:label="t('participationNum')"
min-width="100"
/>
<el-table-column
prop="finally_order_time"
:label="t('orderTime')"
min-width="100"
/>
<el-table-column prop="total_order_money" :label="t('consumptionMoney')" min-width="100" />
<el-table-column prop="total_num" :label="t('participationNum')" min-width="100" />
<el-table-column prop="finally_order_time" :label="t('orderTime')" min-width="100" />
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="memberParams.page"
v-model:page-size="memberParams.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="memberParams.total"
@size-change="getManjianMemberPageListFn()"
@current-change="getManjianMemberPageListFn"
/>
<el-pagination v-model:current-page="memberParams.page" v-model:page-size="memberParams.limit"
layout="total, sizes, prev, pager, next, jumper" :total="memberParams.total"
@size-change="getManjianMemberPageListFn()" @current-change="getManjianMemberPageListFn" />
</div>
</div>
</div>
@ -288,10 +201,7 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue'
import { t } from '@/lang'
import {
getManjianInfo,
getManjianMemberPageList,
} from '@/addon/shop/api/marketing'
import {getManjianInfo, getManjianMemberPageList} from "@/addon/shop/api/marketing";
import { useRouter, useRoute } from 'vue-router'
import { img } from '@/utils/common'
@ -308,14 +218,14 @@ const handleClick = (data: string) => {
}
const handleClose = (done: () => void) => {
activeName.value = 'basicInfo'
showDialog.value = false
activeName.value = 'basicInfo';
showDialog.value = false;
}
const getManjianInfoFn = (id:number)=>{
loading.value = true
const data = {
manjian_id: id,
manjian_id: id
}
getManjianInfo(data).then((res:any)=>{
formData.value = Object.assign(formData.value,res.data.manjian_info)
@ -331,8 +241,8 @@ const memberParams = reactive({
loading: false,
data: [],
searchParam: {
id: id,
},
id: id
}
})
const getManjianMemberPageListFn= (page: number = 1)=>{
memberParams.loading = true
@ -340,14 +250,12 @@ const getManjianMemberPageListFn = (page: number = 1) => {
getManjianMemberPageList({
page: memberParams.page,
limit: memberParams.limit,
...memberParams.searchParam,
})
.then((res: any) => {
...memberParams.searchParam
}).then((res:any)=>{
memberParams.loading = false
memberParams.data = res.data.data
memberParams.total = res.data.total
})
.catch(() => {
}).catch(() => {
memberParams.loading = false
})
}
@ -355,12 +263,12 @@ const getManjianMemberPageListFn = (page: number = 1) => {
//
const detailEvent = (member_id:number)=> {
let routeData = router.resolve(`/member/detail?id=${member_id}`)
window.open(routeData.href, ' blank')
window.open(routeData.href, ' blank');
}
const setFormData = async (row: any = null) => {
id = row.id
memberParams.searchParam.id = row.id
id = row.id;
memberParams.searchParam.id = row.id;
getManjianMemberPageListFn()
getManjianInfoFn(Number(id))
@ -368,7 +276,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData,
setFormData
})
</script>
<style lang="scss">

1089
admin/src/addon/shop/views/marketing/manjian/edit.vue

File diff suppressed because it is too large

381
admin/src/addon/shop/views/marketing/manjian/list.vue

@ -3,119 +3,46 @@
<el-card class="box-card !border-none" shadow="never">
<div class="flex justify-between items-center">
<span class="text-page-title">{{ pageName }}</span>
<el-button type="primary" @click="handleChange">{{
t('addFullDiscountBonus')
}}</el-button>
<el-button type="primary" @click="handleChange">{{ t("addFullDiscountBonus") }}</el-button>
</div>
<!-- 搜索 -->
<el-card
class="box-card !border-none my-[10px] table-search-wrap"
shadow="never"
>
<el-form
:inline="true"
:model="tableData.searchParam"
ref="searchFormRef"
>
<el-card class="box-card !border-none my-[10px] table-search-wrap" shadow="never">
<el-form :inline="true" :model="tableData.searchParam" ref="searchFormRef">
<el-form-item :label="t('name')" prop="manjian_name">
<el-input
v-model.trim="tableData.searchParam.manjian_name"
:placeholder="t('namePlaceholder')"
/>
<el-input v-model.trim="tableData.searchParam.manjian_name" :placeholder="t('namePlaceholder')" />
</el-form-item>
<el-form-item>
<el-form-item :label="t('activityTime')" prop="create_time">
<el-date-picker
v-model="tableData.searchParam.create_time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
:start-placeholder="t('startDate')"
:end-placeholder="t('endDate')"
/>
<el-date-picker v-model="tableData.searchParam.create_time" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')" />
</el-form-item>
<el-button type="primary" @click="loadManjianList()">{{
t('search')
}}</el-button>
<el-button @click="resetForm(searchFormRef)">{{
t('reset')
}}</el-button>
<el-button type="primary" @click="loadManjianList()">{{ t("search") }}</el-button>
<el-button @click="resetForm(searchFormRef)">{{ t("reset") }}</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 列表 -->
<div>
<el-tabs
v-model="tableData.searchParam.status"
class="goods-tabs"
@tab-click="tabHandleClick"
>
<el-tabs v-model="tableData.searchParam.status" class="goods-tabs" @tab-click="tabHandleClick">
<el-tab-pane :label="t('all')" name=""></el-tab-pane>
<el-tab-pane
v-for="(label, value) in statusList"
:key="value"
:label="label"
:name="value"
></el-tab-pane>
<el-tab-pane v-for="(label, value) in statusList" :key="value" :label="label" :name="value"></el-tab-pane>
</el-tabs>
<div class="mb-[10px] flex items-center" v-if="status">
<el-checkbox
v-model="toggleCheckbox"
size="large"
class="px-[14px]"
@change="toggleChange"
:indeterminate="isIndeterminate"
/>
<el-button
@click="batchDeleteManjianFn"
v-if="status == 0 || status == 2 || status == -1"
size="small"
>{{ t('batchDelete') }}</el-button
>
<el-button
@click="batchCloseManjianFn"
v-if="status == 1"
size="small"
>{{ t('batchClose') }}</el-button
>
<el-checkbox v-model="toggleCheckbox" size="large" class="px-[14px]" @change="toggleChange" :indeterminate="isIndeterminate" />
<el-button @click="batchDeleteManjianFn" v-if="status ==0||status ==2 ||status ==-1" size="small">{{t("batchDelete")}}</el-button>
<el-button @click="batchCloseManjianFn" v-if="status==1" size="small">{{t("batchClose")}}</el-button>
</div>
<el-table
:data="tableData.data"
size="large"
v-loading="tableData.loading"
ref="goodBankListTableRef"
@selection-change="handleSelectionChange"
>
<el-table :data="tableData.data" size="large" v-loading="tableData.loading" ref="goodBankListTableRef" @selection-change="handleSelectionChange">
<template #empty>
<span>{{ !tableData.loading ? t('emptyData') : '' }}</span>
<span>{{ !tableData.loading ? t("emptyData") : "" }}</span>
</template>
<el-table-column type="selection" width="55" />
<el-table-column
prop="manjian_name"
:label="t('name')"
min-width="130"
/>
<el-table-column
prop="status_name"
:label="t('status')"
min-width="130"
/>
<el-table-column
prop="total_order_num"
:label="t('activeOrderNum')"
min-width="130"
/>
<el-table-column
prop="total_order_money"
:label="t('activeOrderMoney')"
min-width="130"
/>
<el-table-column
prop="total_member_num"
:label="t('activeMemberNum')"
min-width="130"
/>
<el-table-column prop="manjian_name" :label="t('name')" min-width="130" />
<el-table-column prop="status_name" :label="t('status')" min-width="130" />
<el-table-column prop="total_order_num" :label="t('activeOrderNum')" min-width="130" />
<el-table-column prop="total_order_money" :label="t('activeOrderMoney')" min-width="130" />
<el-table-column prop="total_member_num" :label="t('activeMemberNum')" min-width="130" />
<el-table-column :label="t('activityTime')" min-width="150">
<template #default="{ row }">
<div>
@ -124,52 +51,17 @@
</div>
</template>
</el-table-column>
<el-table-column
:label="t('operation')"
fixed="right"
align="right"
min-width="120"
>
<el-table-column :label="t('operation')" fixed="right" align="right" min-width="120">
<template #default="{ row }">
<el-button
v-if="row.status != '-1' || row.status != '2'"
type="primary"
link
@click="editEvent(row)"
>{{ t('edit') }}</el-button
>
<el-button
type="primary"
link
@click="detailEvent(row.manjian_id)"
>{{ t('detail') }}</el-button
>
<el-button
v-if="row.status == '1'"
type="primary"
link
@click="closeEvent(row.manjian_id)"
>{{ t('close') }}</el-button
>
<el-button
v-if="row.status != '1'"
type="primary"
link
@click="deleteEvent(row.manjian_id)"
>{{ t('delete') }}</el-button
>
<el-button v-if="row.status!='-1'|| row.status!='2'" type="primary" link @click="editEvent(row)">{{t("edit")}}</el-button>
<el-button type="primary" link @click="detailEvent(row.manjian_id)">{{ t('detail') }}</el-button>
<el-button v-if="row.status=='1'" type="primary" link @click="closeEvent(row.manjian_id)">{{ t('close') }}</el-button>
<el-button v-if="row.status!='1'" type="primary" link @click="deleteEvent(row.manjian_id)">{{ t("delete") }}</el-button>
</template>
</el-table-column>
</el-table>
<div class="mt-[16px] flex justify-end">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.limit"
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="loadManjianList()"
@current-change="loadManjianList"
/>
<el-pagination v-model:current-page="tableData.page" v-model:page-size="tableData.limit" layout="total, sizes, prev, pager, next, jumper" :total="tableData.total" @size-change="loadManjianList()" @current-change="loadManjianList" />
</div>
</div>
</el-card>
@ -178,26 +70,19 @@
</template>
<script lang="ts" setup>
//
import { ref, reactive } from 'vue'
import { t } from '@/lang'
import { useRoute, useRouter } from 'vue-router'
import {
closeManjian,
deleteManjian,
getManjianList,
getManjianStatusList,
batchCloseMajian,
batchDeleteManjian,
} from '@/addon/shop/api/marketing'
import { FormInstance, ElMessageBox, ElMessage } from 'element-plus'
import { ref, reactive } from "vue";
import {t} from "@/lang";
import { useRoute, useRouter } from "vue-router";
import {closeManjian, deleteManjian,getManjianList,getManjianStatusList,batchCloseMajian,batchDeleteManjian} from "@/addon/shop/api/marketing";
import { FormInstance, ElMessageBox,ElMessage } from "element-plus";
import manjianDetail from '@/addon/shop/views/marketing/manjian/detail.vue'
import { setTablePageStorage, getTablePageStorage } from '@/utils/common'
import { setTablePageStorage,getTablePageStorage } from "@/utils/common";
const router = useRouter()
const route = useRoute()
const pageName = route.meta.title
const repeat = ref(false)
const searchFormRef = ref<FormInstance>()
const router = useRouter();
const route = useRoute();
const pageName = route.meta.title;
const repeat = ref(false);
const searchFormRef = ref<FormInstance>();
//
const tableData = reactive({
page: 1,
@ -207,10 +92,10 @@ const tableData = reactive({
data: [],
searchParam: {
create_time: [],
manjian_name: '',
manjian_name: "",
status: route.query.status || '',
},
})
});
const status = ref()
// tab
@ -222,36 +107,30 @@ const tabHandleClick = (tab: any, event: Event) => {
//
const loadManjianList = (page: number = 1) => {
tableData.loading = true
tableData.page = page
tableData.loading = true;
tableData.page = page;
getManjianList({
page: tableData.page,
limit: tableData.limit,
...tableData.searchParam,
})
.then((res) => {
tableData.loading = false
tableData.data = res.data.data
tableData.total = res.data.total
setTablePageStorage(
tableData.page,
tableData.limit,
tableData.searchParam
)
})
.catch(() => {
tableData.loading = false
})
}
}).then((res) => {
tableData.loading = false;
tableData.data = res.data.data;
tableData.total = res.data.total;
setTablePageStorage(tableData.page, tableData.limit, tableData.searchParam);
}).catch(() => {
tableData.loading = false;
});
};
loadManjianList(getTablePageStorage(tableData.searchParam).page)
loadManjianList(getTablePageStorage(tableData.searchParam).page);
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
loadManjianList()
}
if (!formEl) return;
formEl.resetFields();
loadManjianList();
};
const handleChange = () => {
router.push('/shop/marketing/manjian/edit')
@ -260,7 +139,7 @@ const handleChange = () => {
const statusList = ref<{ [key: string]: string }>({})
const getManjianStatusListFn = () => {
getManjianStatusList().then((res) => {
statusList.value = res.data
statusList.value= res.data;
})
}
@ -273,73 +152,75 @@ const editEvent = (data: any) => {
//
const manjianDetailDialog: Record<string, any> | null = ref(null)
const detailEvent=(id:number)=>{
let data = { id: id }
manjianDetailDialog.value.setFormData(data)
manjianDetailDialog.value.showDialog = true
let data = {id: id};
manjianDetailDialog.value.setFormData(data);
manjianDetailDialog.value.showDialog = true;
}
//
const toggleCheckbox = ref()
const toggleCheckbox = ref();
//
const isIndeterminate = ref(false)
const isIndeterminate = ref(false);
//
const toggleChange = (value: any) => {
isIndeterminate.value = false
goodBankListTableRef.value.toggleAllSelection()
}
isIndeterminate.value = false;
goodBankListTableRef.value.toggleAllSelection();
};
const goodBankListTableRef = ref()
const goodBankListTableRef = ref();
//
const multipleSelection: any = ref([])
const multipleSelection: any = ref([]);
//
const handleSelectionChange = (val: []) => {
multipleSelection.value = val
multipleSelection.value = val;
toggleCheckbox.value = false
toggleCheckbox.value = false;
if (
multipleSelection.value.length > 0 &&
multipleSelection.value.length < tableData.data.length
) {
isIndeterminate.value = true
isIndeterminate.value = true;
} else {
isIndeterminate.value = false
isIndeterminate.value = false;
}
if (multipleSelection.value.length == tableData.data.length) {
toggleCheckbox.value = true
}
toggleCheckbox.value = true;
}
};
//
const closeEvent = (id:number)=>{
ElMessageBox.confirm(t('closeTips'), t('warning'), {
ElMessageBox.confirm(t('closeTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
closeManjian(id)
.then(() => {
type: 'warning'
}
).then(() => {
closeManjian(id).then(() => {
loadManjianList()
}).catch(() => {
})
.catch(() => {})
})
}
//
const deleteEvent = (id:number) => {
ElMessageBox.confirm(t('deleteTips'), t('warning'), {
ElMessageBox.confirm(t('deleteTips'), t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
}).then(() => {
deleteManjian(id)
.then(() => {
type: 'warning'
}
).then(() => {
deleteManjian(id).then(() => {
loadManjianList()
}).catch(() => {
})
.catch(() => {})
})
}
@ -347,69 +228,67 @@ const deleteEvent = (id: number) => {
const batchDeleteManjianFn = () => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedActiveDeleteTips')}`,
})
return
type: "warning",
message: `${ t("batchEmptySelectedActiveDeleteTips") }`,
});
return;
}
ElMessageBox.confirm(t('batchGoodsDeleteTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
ElMessageBox.confirm(t("batchGoodsDeleteTips"), t("warning"), {
confirmButtonText: t("confirm"),
cancelButtonText: t("cancel"),
type: "warning",
}).then(() => {
if (repeat.value) return
repeat.value = true
if (repeat.value) return;
repeat.value = true;
const manjian_id: any = []
const manjian_id: any = [];
multipleSelection.value.forEach((item: any) => {
manjian_id.push(item.manjian_id)
})
manjian_id.push(item.manjian_id);
});
batchDeleteManjian({ manjian_id })
.then(() => {
loadManjianList()
repeat.value = false
})
.catch(() => {
repeat.value = false
})
})
}
batchDeleteManjian({ manjian_id }).then(() => {
loadManjianList();
repeat.value = false;
}).catch(() => {
repeat.value = false;
});
});
};
//
const batchCloseManjianFn = () => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedActiveCloseTips')}`,
})
return
type: "warning",
message: `${ t("batchEmptySelectedActiveCloseTips") }`,
});
return;
}
ElMessageBox.confirm(t('batchGoodsCloseTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning',
ElMessageBox.confirm(t("batchGoodsCloseTips"), t("warning"), {
confirmButtonText: t("confirm"),
cancelButtonText: t("cancel"),
type: "warning",
}).then(() => {
if (repeat.value) return
repeat.value = true
if (repeat.value) return;
repeat.value = true;
const manjian_id: any = []
const manjian_id: any = [];
multipleSelection.value.forEach((item: any) => {
manjian_id.push(item.manjian_id)
})
manjian_id.push(item.manjian_id);
});
batchCloseMajian({ manjian_id }).then(() => {
loadManjianList();
repeat.value = false;
}).catch(() => {
repeat.value = false;
});
});
};
batchCloseMajian({ manjian_id })
.then(() => {
loadManjianList()
repeat.value = false
})
.catch(() => {
repeat.value = false
})
})
}
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
</style>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save