Browse Source

新增员工管理和校区管理

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

3
admin/.eslintignore

@ -0,0 +1,3 @@
node_modules/
dist/
public/

39
admin/.eslintrc.js

@ -0,0 +1,39 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: false,
},
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'plugin:prettier/recommended',
],
rules: {
// Vue 推荐规则
'vue/multi-word-component-names': 0,
'vue/require-default-prop': 0,
// TypeScript 常用规则
'@typescript-eslint/no-explicit-any': ['warn'],
'@typescript-eslint/no-unused-vars': ['error'],
// 缩进设置(可选)
indent: ['error', 2],
quotes: ['error', 'single'],
semi: ['error', 'never'],
},
}

7
admin/.prettierignore

@ -0,0 +1,7 @@
node_modules
dist
.env
*.md
*.png
*.jpg
*.ico

11
admin/.prettierrc

@ -0,0 +1,11 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "auto"
}

2
admin/auto-imports.d.ts

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

15
admin/package.json

@ -6,7 +6,10 @@
"scripts": {
"dev": "vite",
"build": "vite build && node publish.cjs",
"preview": "vite preview"
"preview": "vite preview",
"lint": "eslint .",
"lint:fix": "eslint . --ext .ts --ext .vue --ext .js --fix",
"format": "prettier --write src/**/*.{ts,vue,js,json}"
},
"dependencies": {
"@element-plus/icons-vue": "2.0.10",
@ -38,21 +41,25 @@
"@tailwindcss/line-clamp": "0.4.2",
"@types/qrcode": "1.5.0",
"@types/sortablejs": "1.15.0",
"@typescript-eslint/eslint-plugin": "5.53.0",
"@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.32.1",
"@vitejs/plugin-vue": "4.0.0",
"autoprefixer": "10.4.13",
"eslint": "8.34.0",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.5",
"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": "9.9.0",
"eslint-plugin-vue": "^10.1.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,11 +22,15 @@ 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,7 +34,10 @@ 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,
})
}
/**
@ -45,7 +48,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,
})
}
@ -55,10 +58,12 @@ 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,
})
}
/********************************* 运费模版 ***************************************/
/**
*
@ -84,7 +89,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}`)
}
/**
@ -93,7 +98,10 @@ 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,
})
}
/**
@ -104,7 +112,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,
})
}
@ -116,11 +124,10 @@ 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,
})
}
/********************************* 自提门店 ***************************************/
/**
*
@ -137,7 +144,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}`)
}
/**
@ -146,7 +153,10 @@ 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,
})
}
/**
@ -157,7 +167,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,
})
}
@ -167,7 +177,10 @@ 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,
})
}
/********************************* 物流查询 ***************************************/
@ -177,7 +190,10 @@ 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,
})
}
/**
@ -220,7 +236,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}`)
}
/**
@ -229,7 +245,9 @@ 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,
})
}
/**
@ -238,7 +256,9 @@ 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,
})
}
/**
@ -255,7 +275,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,7 +24,10 @@ 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,
})
}
/**
@ -32,7 +35,10 @@ 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,
})
}
/**
@ -40,7 +46,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 })
}
/**
@ -49,7 +55,10 @@ 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,
})
}
/**
@ -59,7 +68,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,
})
}
@ -68,7 +77,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 })
}
/**
@ -77,7 +86,10 @@ 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,
})
}
/**
@ -95,7 +107,10 @@ 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,
})
}
/**
@ -119,7 +134,9 @@ 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,
})
}
/**
@ -163,7 +180,9 @@ 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,
})
}
/**
@ -172,7 +191,9 @@ 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,
})
}
/**
@ -181,7 +202,9 @@ 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,
})
}
/**
@ -189,7 +212,7 @@ export function editGoodsListMemberPrice(params: Record<string, any>) {
* @returns
*/
export function getGoodsType() {
return request.get(`shop/goods/type`);
return request.get(`shop/goods/type`)
}
/**
@ -216,7 +239,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}`)
}
/**
@ -225,7 +248,10 @@ 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,
})
}
/**
@ -236,7 +262,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,
})
}
@ -245,7 +271,9 @@ 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,
})
}
/**
@ -254,7 +282,10 @@ 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,
})
}
/**
@ -262,7 +293,9 @@ 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,
})
}
/**
@ -289,7 +322,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}`)
}
/**
@ -298,7 +331,10 @@ 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,
})
}
/**
@ -309,7 +345,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,
})
}
@ -319,7 +355,10 @@ 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,
})
}
/**
@ -327,7 +366,9 @@ 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,
})
}
/**
@ -354,7 +395,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}`)
}
/**
@ -363,7 +404,10 @@ 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,
})
}
/**
@ -374,7 +418,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,
})
}
@ -383,7 +427,9 @@ 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,
})
}
/**
@ -392,7 +438,10 @@ 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,
})
}
/**
@ -419,7 +468,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}`)
}
/**
@ -428,7 +477,10 @@ 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,
})
}
/**
@ -439,7 +491,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,
})
}
@ -449,7 +501,10 @@ 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,
})
}
/**
@ -475,7 +530,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}`)
}
/**
@ -484,7 +539,10 @@ 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,
})
}
/**
@ -495,7 +553,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,
})
}
@ -505,7 +563,10 @@ 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,
})
}
/**
@ -514,7 +575,10 @@ 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,
})
}
/**
@ -523,7 +587,10 @@ 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,
})
}
/**
@ -531,7 +598,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`)
}
/**
@ -566,7 +633,10 @@ 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,
})
}
/**
@ -575,7 +645,10 @@ 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,
})
}
/**
@ -586,7 +659,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,
})
}
@ -598,7 +671,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,
})
}
@ -608,10 +681,14 @@ 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,
}
)
}
/**
@ -622,7 +699,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,
})
}
@ -634,7 +711,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,
})
}
@ -662,7 +739,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}`)
}
/**
@ -671,7 +748,10 @@ 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,
})
}
/**
@ -682,7 +762,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,
})
}
@ -692,7 +772,10 @@ 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,
})
}
/**
@ -700,7 +783,9 @@ 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,
})
}
/**
@ -708,7 +793,9 @@ 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,
})
}
/**
@ -716,7 +803,9 @@ 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,
})
}
/**
@ -732,5 +821,7 @@ 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,7 +24,10 @@ 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,
})
}
/**
@ -59,7 +62,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 })
}
/**
@ -68,7 +71,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}`)
}
/**
@ -77,7 +80,9 @@ 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,
})
}
/**
@ -88,7 +93,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,
})
}
@ -107,7 +112,9 @@ 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,
})
}
/**
@ -143,7 +150,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}`)
}
/**
@ -152,7 +159,10 @@ 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,
})
}
/**
@ -163,7 +173,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,
})
}
@ -173,10 +183,14 @@ 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,
}
)
}
/**
@ -185,7 +199,9 @@ 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,
})
}
/**
@ -194,7 +210,9 @@ 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,
})
}
/**
@ -203,7 +221,9 @@ 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,
})
}
/**
@ -212,7 +232,9 @@ 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,
})
}
/**
@ -220,7 +242,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`)
}
/**
@ -231,7 +253,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,
})
}
@ -252,7 +274,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}`)
}
/**
@ -261,7 +283,10 @@ 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,
})
}
/**
@ -272,7 +297,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,
})
}
@ -284,7 +309,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,
})
}
@ -294,7 +319,9 @@ 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,
})
}
/**
@ -312,7 +339,7 @@ export function getActiveExchangeStatus() {
* @returns
*/
export function getActiveNewcomerConfig() {
return request.get(`shop/active/newcomer/config`);
return request.get(`shop/active/newcomer/config`)
}
/**
@ -323,7 +350,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,
})
}
@ -353,7 +380,10 @@ 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,
})
}
/**
@ -387,7 +417,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}`)
}
/**
@ -396,7 +426,10 @@ 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,
})
}
/**
@ -407,7 +440,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,
})
}
@ -419,7 +452,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,
})
}
@ -438,7 +471,9 @@ 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,
})
}
/**
@ -447,7 +482,9 @@ 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,
})
}
/**
@ -502,7 +539,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 })
}
/**
@ -529,10 +566,14 @@ 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,
}
)
}
/**
@ -568,7 +609,9 @@ 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,
})
}
/**
@ -577,7 +620,9 @@ 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,
})
}
/**
@ -586,5 +631,7 @@ 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,7 +125,10 @@ 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
)
}
/**
@ -133,7 +136,10 @@ 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
)
}
/**
@ -147,7 +153,9 @@ 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,
})
}
/**
@ -190,7 +198,9 @@ 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,
})
}
/**
@ -230,7 +240,10 @@ 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,7 +24,10 @@ 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,
})
}
/**
@ -33,7 +36,10 @@ 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,
})
}
/**
@ -42,7 +48,10 @@ 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,
})
}
/**
@ -50,7 +59,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')
}
/**
@ -58,5 +67,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,5 +40,4 @@
"printStyleTips1": "主流快递单打印纸尺寸一般为:76*130,100*180(单位mm)",
"printStyleTips2": "不填写则取物流公司的默认模板",
"examine": "点击查看"
}

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

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

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

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

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

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

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

@ -4,55 +4,140 @@
<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>
@ -63,7 +148,11 @@
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'
@ -74,14 +163,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()
@ -90,17 +179,19 @@ 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 = () => {
@ -123,7 +214,7 @@ const initMap = () => {
map = new TMap.Map('container', {
center,
zoom: 14
zoom: 14,
})
map.on('tilesloaded', () => {
@ -136,7 +227,7 @@ const initMap = () => {
map.setCenter(evt.latLng)
marker.updateGeometries({
id: 'center',
position: evt.latLng
position: evt.latLng,
})
latLngChange(evt.latLng.lat, evt.latLng.lng)
})
@ -147,11 +238,12 @@ 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
@ -165,7 +257,8 @@ const latLngChange = (lat: number, lng: number) => {
} else {
console.error(message, result)
}
}).catch(err => {
})
.catch((err) => {
console.log(err)
})
}
@ -183,11 +276,11 @@ const initialFormData = {
address: '',
full_address: '',
lat: 39.908626,
lng: 116.397190,
lng: 116.39719,
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 })
@ -215,11 +308,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' },
@ -230,8 +323,8 @@ const formRules = computed(() => {
callback(new Error(t('mobileTips')))
}
callback()
}
}
},
},
],
address_area: [
{
@ -246,21 +339,23 @@ 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
@ -283,14 +378,17 @@ watch(() => formData.province_id, (nval) => {
} 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
@ -313,73 +411,91 @@ watch(() => formData.city_id, (nval) => {
} 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
@ -392,15 +508,17 @@ 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,7 +1,6 @@
<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">
@ -9,56 +8,109 @@
</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>
@ -66,10 +118,13 @@
<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
@ -82,8 +137,8 @@ const shopAddressTable = reactive({
data: [],
searchParam: {
mobile: '',
full_address: ''
}
full_address: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -98,17 +153,23 @@ 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()
@ -131,17 +192,16 @@ 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(() => {})
})
}
@ -152,5 +212,4 @@ 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,7 +1,6 @@
<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')">
@ -11,53 +10,111 @@
<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>
@ -71,7 +128,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()
@ -88,8 +145,8 @@ const companyTable = reactive({
logo: '',
url: '',
create_time: '',
modify_time: ''
}
modify_time: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -104,18 +161,24 @@ 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)
/**
* 添加物流公司
@ -136,17 +199,16 @@ 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(() => {})
})
}
@ -157,5 +219,4 @@ 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,112 +1,267 @@
<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>
@ -116,7 +271,11 @@
<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'
@ -139,10 +298,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)
@ -152,9 +311,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]
@ -163,9 +322,9 @@ const getCompanyInfoFn = ()=>{
})
}
if (formData.company_id) {
getCompanyInfoFn();
getCompanyInfoFn()
} else {
loading.value = false;
loading.value = false
}
const formRef = ref<FormInstance>()
@ -174,7 +333,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: [
{
@ -182,25 +341,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) {
@ -210,8 +369,8 @@ const formRules = computed(() => {
callback(new Error(t('expTypeValueRepeatTips')))
}
callback()
}
}
},
},
],
print_style: [
{
@ -219,22 +378,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) {
@ -244,9 +403,9 @@ const formRules = computed(() => {
callback(new Error(t('printStyleSizeRepeatTips')))
}
callback()
}
}
]
},
},
],
}
})
@ -254,7 +413,7 @@ const formRules = computed(() => {
const addPrintStyleValueEvent = () => {
formData.print_style.push({
template_name: '',
template_size: ''
template_size: '',
})
}
@ -262,7 +421,7 @@ const addPrintStyleValueEvent = ()=>{
const addExpTypeValueEvent = () => {
formData.exp_type.push({
text: '',
value: ''
value: '',
})
}
@ -286,22 +445,21 @@ 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,17 +1,50 @@
<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>
@ -21,7 +54,11 @@
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)
@ -33,7 +70,7 @@ const loading = ref(false)
const initialFormData = {
deliver_id: '',
deliver_name: '',
deliver_mobile: ''
deliver_mobile: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -43,17 +80,21 @@ 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',
},
],
}
})
@ -73,11 +114,13 @@ 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
})
}
@ -100,7 +143,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData
setFormData,
})
</script>

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

@ -3,7 +3,12 @@
<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">
@ -11,29 +16,76 @@
<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>
@ -47,7 +99,10 @@
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()
@ -62,13 +117,15 @@ 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
})
}
@ -90,7 +147,7 @@ const rowDrop = () => {
rowDrop()
})
update()
}
},
})
}
@ -102,7 +159,10 @@ 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
@ -113,7 +173,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,7 +1,6 @@
<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">
@ -9,72 +8,152 @@
</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>
@ -82,14 +161,18 @@
<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,
@ -98,9 +181,9 @@ const tableData = reactive({
loading: true,
data: [],
searchParam: {
template_name: "",
express_company_id: "",
}
template_name: '',
express_company_id: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -119,12 +202,14 @@ 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
})
}
@ -134,9 +219,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
})
/**
@ -158,13 +243,11 @@ 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()
})
@ -174,13 +257,11 @@ 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()
})
@ -194,5 +275,4 @@ 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,17 +1,32 @@
<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>
@ -22,29 +37,47 @@
</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">
@ -52,42 +85,78 @@
<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>
@ -96,11 +165,14 @@ 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) => {
@ -113,7 +185,7 @@ const formData:any = reactive({
kdniao_api_key: '',
server_port1: '8000',
server_port2: '18000',
https_port: '8443'
https_port: '8443',
})
const setFormData = async () => {
@ -154,24 +226,25 @@ 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 {
@ -179,6 +252,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,85 +4,179 @@
<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>
@ -92,24 +186,35 @@
<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>
@ -125,7 +230,7 @@
addElectronicSheet,
editElectronicSheet,
getElectronicSheetInfo,
getElectronicSheetPayType
getElectronicSheetPayType,
} from '@/addon/shop/api/electronic_sheet'
import { getCompanyList } from '@/addon/shop/api/delivery'
@ -164,11 +269,19 @@
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',
},
],
}
})
@ -179,18 +292,19 @@
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
@ -199,39 +313,38 @@
}
}
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
}
}
}
/**
@ -244,18 +357,19 @@
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,7 +5,14 @@
</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">
@ -43,11 +50,32 @@
<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>
{{
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 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')">
@ -56,22 +84,42 @@
<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>
@ -80,15 +128,27 @@
<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>
@ -97,36 +157,89 @@
<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>
@ -136,7 +249,12 @@
</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>
@ -149,7 +267,13 @@ 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'
@ -168,16 +292,18 @@ 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',
@ -188,7 +314,7 @@ const formData = ref({
base_price: '',
grad_dist: '',
grad_price: '',
weight_start: 0.000,
weight_start: 0.0,
weight_unit: 0,
weight_price: 0,
area: [
@ -198,16 +324,18 @@ 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) => {
@ -215,8 +343,8 @@ const formRules = computed(() => {
callback(new Error(t('defaultDeliveryAddressEmpty')))
}
callback()
}
}
},
},
],
delivery_type: [
{
@ -225,8 +353,8 @@ const formRules = computed(() => {
callback(new Error(t('deliveryTypeRequire')))
}
callback()
}
}
},
},
],
distance: [
{
@ -247,10 +375,12 @@ const formRules = computed(() => {
}
callback()
},
trigger: 'blur'
}
trigger: 'blur',
},
],
area_name: [
{ required: true, message: t('areaNameRequire'), trigger: 'blur' },
],
area_name: [{ required: true, message: t('areaNameRequire'), trigger: 'blur' }],
start_price: [
{ required: true, message: t('startPriceRequire'), trigger: 'blur' },
{
@ -260,11 +390,15 @@ 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) {
@ -272,8 +406,8 @@ const formRules = computed(() => {
}
callback()
},
trigger: 'blur'
}
trigger: 'blur',
},
],
area: [
{
@ -283,25 +417,31 @@ 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 = () => {
@ -319,11 +459,14 @@ 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)
@ -331,8 +474,10 @@ 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)
})
}
@ -348,8 +493,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)
@ -373,7 +518,9 @@ 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(() => {
@ -400,15 +547,17 @@ 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,9 +1,20 @@
<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>
@ -11,10 +22,23 @@
<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">-->
@ -23,7 +47,11 @@
</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>
@ -32,18 +60,29 @@
<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">-->
@ -61,13 +100,14 @@
<!-- </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>
@ -100,7 +140,7 @@ const formData = reactive<formDataType|any>({
kdniao_app_key: '',
kdniao_is_pay: 1,
kd100_app_key: '',
kd100_customer: ''
kd100_customer: '',
})
const setFormData = async () => {
@ -120,8 +160,7 @@ const openEvent = (url:any) => {
const formRef = ref<FormInstance>()
//
const formRules = reactive<FormRules>({
})
const formRules = reactive<FormRules>({})
/**
* 保存
@ -132,20 +171,21 @@ 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 {
@ -153,6 +193,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,43 +14,84 @@
{{ 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>
@ -72,8 +113,8 @@ const tableData = reactive({
data: [],
searchParam: {
deliver_name: '',
deliver_mobile: ''
}
deliver_mobile: '',
},
})
const searchFormRef = ref<FormInstance>()
/**
@ -86,12 +127,14 @@ 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
})
}
@ -118,17 +161,16 @@ 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,30 +15,55 @@
</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>
@ -46,30 +71,57 @@
</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>
@ -81,7 +133,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
@ -94,8 +146,8 @@ const storeTable = reactive({
data: [],
searchParam: {
store_name: '',
create_time: ''
}
create_time: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -110,17 +162,23 @@ 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()
@ -143,17 +201,16 @@ 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(() => {})
})
}
@ -164,5 +221,4 @@ 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,59 +1,132 @@
<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>
@ -75,14 +148,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()
@ -91,17 +164,19 @@ 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 = () => {
@ -124,7 +199,7 @@ const initMap = () => {
map = new TMap.Map('container', {
center,
zoom: 14
zoom: 14,
})
map.on('tilesloaded', () => {
@ -137,7 +212,7 @@ const initMap = () => {
map.setCenter(evt.latLng)
marker.updateGeometries({
id: 'center',
position: evt.latLng
position: evt.latLng,
})
latLngChange(evt.latLng.lat, evt.latLng.lng)
})
@ -148,11 +223,12 @@ 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
@ -166,7 +242,8 @@ const latLngChange = (lat: number, lng: number) => {
} else {
console.error(message, result)
}
}).catch(err => {
})
.catch((err) => {
console.log(err)
})
}
@ -188,9 +265,9 @@ const initialFormData = {
district_name: '',
address: '',
full_address: '',
longitude: 116.397190,
longitude: 116.39719,
latitude: 39.908626,
trade_time: ''
trade_time: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -212,16 +289,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: [
{
@ -236,21 +313,23 @@ 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
@ -273,14 +352,17 @@ watch(() => formData.province_id, (nval) => {
} 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
@ -303,73 +385,91 @@ watch(() => formData.city_id, (nval) => {
} 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
@ -378,22 +478,30 @@ 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,7 +1,6 @@
<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')">
@ -16,42 +15,93 @@
</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>
@ -61,10 +111,13 @@
<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()
@ -77,8 +130,8 @@ const templateTable = reactive({
loading: true,
data: [],
searchParam: {
template_name: ''
}
template_name: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -93,17 +146,23 @@ 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)
/**
* 添加运费模板
@ -117,24 +176,26 @@ 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(() => {})
})
}
@ -145,5 +206,4 @@ 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,9 +5,22 @@
</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">
@ -21,119 +34,239 @@
<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>
@ -143,7 +276,11 @@
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'
@ -166,7 +303,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 })
@ -175,7 +312,8 @@ 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]
@ -185,24 +323,31 @@ 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 }],
}
})
@ -272,16 +417,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]
})
@ -290,14 +435,21 @@ 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[]>([])
@ -311,10 +463,22 @@ 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: '' })
@ -372,7 +536,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)
})
@ -386,7 +550,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)
@ -400,11 +564,13 @@ 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
@ -434,44 +600,71 @@ 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,22 +1,35 @@
<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>
@ -24,88 +37,160 @@
<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>
@ -113,7 +198,6 @@
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>
@ -137,26 +221,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
@ -164,7 +248,7 @@ diyStore.editComponent.verify = (index: number) => {
const selectCouponStyle = reactive({
title: diyStore.editComponent.styleName,
value: diyStore.editComponent.style
value: diyStore.editComponent.style,
})
//
@ -172,76 +256,84 @@ 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,21 +6,33 @@
<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">
@ -30,33 +42,78 @@
</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>
@ -69,10 +126,17 @@
<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>
@ -81,56 +145,107 @@
</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>
@ -143,47 +258,108 @@
<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>
@ -233,33 +409,32 @@ 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([
@ -267,20 +442,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) => {
@ -295,10 +470,12 @@ 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
})
}
@ -314,7 +491,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
}
@ -344,7 +521,6 @@ const setRowSelection = ()=>{
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

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

@ -12,8 +12,17 @@
<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>
@ -28,21 +37,41 @@
</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">
@ -56,55 +85,121 @@
</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>
@ -116,10 +211,17 @@
<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>
@ -128,16 +230,34 @@
</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>
@ -150,10 +270,17 @@
<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>
@ -162,56 +289,107 @@
</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>
@ -220,46 +398,94 @@
<!-- 样式 -->
<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>
@ -288,7 +514,6 @@ 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 == '') {
@ -302,7 +527,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
@ -325,8 +550,8 @@ const firstCategoryTable = reactive({
loading: true,
data: [],
searchParam: {
level: 1
}
level: 1,
},
})
const firstCategoryTableRef = ref<InstanceType<typeof ElTable>>()
@ -338,18 +563,20 @@ 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
}
@ -362,28 +589,27 @@ 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
@ -391,7 +617,7 @@ const changeBtnStyle = (item:any) => {
const clearCategory = () => {
diyStore.editComponent.goods_category = ''
diyStore.editComponent.goods_category_name = '';
diyStore.editComponent.goods_category_name = ''
}
//
@ -407,7 +633,7 @@ const goodsBoxRef = ref()
const categoryTable = reactive({
loading: true,
data: []
data: [],
})
onMounted(() => {
@ -419,16 +645,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()
})
)
}
},
})
})
})
@ -442,16 +668,18 @@ 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]
@ -461,13 +689,15 @@ 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(() => {
@ -480,11 +710,16 @@ 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)
}
@ -496,18 +731,17 @@ 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,9 +30,16 @@
</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'">
@ -49,17 +56,34 @@
<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>
@ -69,24 +93,50 @@
<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>
@ -131,7 +181,6 @@ 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,7 +32,6 @@ 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,17 +4,32 @@
<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')">
@ -22,18 +37,38 @@
</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')">
@ -41,22 +76,42 @@
</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>
@ -67,10 +122,22 @@
<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>
@ -100,7 +167,7 @@ diyStore.editComponent.verify = (index: number) => {
res.message = t('请选择榜单')
}
}
});
})
return res
}
@ -109,21 +176,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,16 +2,29 @@
<!-- 内容 -->
<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>
@ -19,40 +32,105 @@
<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>
@ -63,13 +141,29 @@
<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>
@ -111,7 +205,10 @@ 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
@ -130,21 +227,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)
})
})
@ -153,33 +250,32 @@ 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,7 +46,6 @@ 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,28 +5,43 @@
<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>
@ -36,32 +51,78 @@
<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>
@ -71,23 +132,58 @@
<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>
@ -95,15 +191,35 @@
<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>
@ -141,20 +257,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
}
@ -162,125 +278,134 @@ 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,7 +5,13 @@
<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>
@ -14,21 +20,32 @@
<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">
@ -55,7 +72,14 @@
<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">
@ -64,14 +88,16 @@
</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>
@ -109,7 +135,6 @@ const changeStyle = () => {
}
defineExpose({})
</script>
<style lang="scss" scoped></style>

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

@ -1,16 +1,20 @@
<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>
<!-- 样式 -->
@ -18,7 +22,6 @@
<!-- 组件样式 -->
<slot name="style"></slot>
</div>
</template>
<script lang="ts" setup>

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

@ -5,7 +5,11 @@
<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>
@ -17,18 +21,33 @@
<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>
@ -36,25 +55,41 @@
<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>
@ -62,14 +97,21 @@
<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>
@ -79,16 +121,34 @@
</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>
@ -101,10 +161,17 @@
<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>
@ -113,8 +180,12 @@
</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>
@ -125,7 +196,11 @@
<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>
@ -133,10 +208,22 @@
<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>
@ -144,10 +231,18 @@
<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>
@ -155,27 +250,57 @@
<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>
@ -199,26 +324,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
}
@ -226,8 +351,8 @@ const titleStyleList = reactive([
{
url: 'addon/shop/diy/single_recommend/title_style_01.png',
title: '风格1',
value:'style-1'
}
value: 'style-1',
},
])
const addImageAd = () => {
@ -236,7 +361,7 @@ const addImageAd = () => {
imageUrl: '',
imgWidth: 0,
imgHeight: 0,
link: { name: '' }
link: { name: '' },
})
}
@ -256,7 +381,7 @@ diyStore.editComponent.verify = (index: number) => {
res.message = t('imageUrlTip')
return res
}
});
})
return res
}
@ -279,10 +404,12 @@ 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
})
}
@ -298,7 +425,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,7 +1,6 @@
<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">
@ -9,70 +8,150 @@
</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>
@ -82,11 +161,17 @@ 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,
@ -97,8 +182,8 @@ const goodsAttrTable = reactive({
searchParam: {
attr_name: '',
order: '',
sort: ''
}
sort: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -110,7 +195,7 @@ const titleDialog = ref('')
const formData = reactive({
attr_id: 0,
attr_name: '',
sort: 0
sort: 0,
})
const formRef = ref<FormInstance>()
@ -119,8 +204,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' },
],
}
})
@ -149,12 +234,14 @@ 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
})
}
@ -165,19 +252,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
}
@ -192,11 +279,13 @@ 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
})
}
@ -215,17 +304,16 @@ 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(() => {})
})
}
@ -234,7 +322,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !/^\d{0,8}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${ t('sortTips') }`
message: `${t('sortTips')}`,
})
return
}
@ -243,9 +331,8 @@ const sortInputListener = debounce((sort, row) => {
}
modifyAttrSort({
attr_id: row.attr_id,
sort
}).then((res) => {
})
sort,
}).then((res) => {})
})
const resetForm = (formEl: FormInstance | undefined) => {
@ -255,5 +342,4 @@ 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,13 +1,20 @@
<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>
@ -17,20 +24,38 @@
<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>
@ -44,11 +69,18 @@
</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>
@ -57,100 +89,229 @@
</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>
@ -166,7 +327,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
@ -177,7 +338,7 @@ const maxLength = ref(30) // 参数值最大数量控制
const goodsAttrTable = reactive({
loading: true,
data: []
data: [],
})
const loadAttrInfo = () => {
@ -185,7 +346,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 = []
}
@ -193,7 +354,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
})
@ -207,23 +368,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
}
@ -237,11 +398,13 @@ 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
})
}
@ -258,15 +421,17 @@ 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
})
})
@ -281,40 +446,44 @@ 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
}
@ -323,26 +492,27 @@ 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) => {})
})
//
@ -350,24 +520,28 @@ 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',
},
],
}
})
@ -380,13 +554,19 @@ 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
}
}
}
@ -396,29 +576,34 @@ 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
})
@ -428,9 +613,8 @@ 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,7 +1,6 @@
<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">
@ -9,57 +8,109 @@
</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>
@ -71,7 +122,11 @@
<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'
@ -89,8 +144,8 @@ const brandTable = reactive({
searchParam: {
brand_name: '',
order: '',
sort: ''
}
sort: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -120,12 +175,14 @@ 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
})
}
@ -154,17 +211,16 @@ 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(() => {})
})
}
//
@ -172,7 +228,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !/^\d{0,8}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${ t('sortTips') }`
message: `${t('sortTips')}`,
})
return
}
@ -181,9 +237,8 @@ const sortInputListener = debounce((sort, row) => {
}
modifyBrandSort({
brand_id: row.brand_id,
sort
}).then((res) => {
})
sort,
}).then((res) => {})
})
const resetForm = (formEl: FormInstance | undefined) => {
@ -193,5 +248,4 @@ 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,12 +7,29 @@
{{ 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>
@ -25,10 +42,17 @@
<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>
@ -37,18 +61,33 @@
</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>
@ -57,14 +96,18 @@
<!-- 商品分类推广弹出框 -->
<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'
@ -79,7 +122,7 @@ const pageName = route.meta.title
const tableRef = useTemplateRefsList<HTMLElement>()
const categoryTable = reactive({
loading: true,
data: []
data: [],
})
onMounted(() => {
@ -93,21 +136,25 @@ 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] //
@ -120,25 +167,35 @@ 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 })
}
},
})
}
@ -152,7 +209,9 @@ 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] || [])
})
@ -168,15 +227,17 @@ 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()
})
}
@ -210,17 +271,22 @@ 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(() => {})
})
}
@ -236,7 +302,6 @@ const router = useRouter()
const handleClick = (path: string) => {
router.push({ path })
}
</script>
<style lang="scss" scoped>
@ -250,9 +315,7 @@ const handleClick = (path: string) => {
.el-table__placeholder {
order: 1;
}
}
}
}
</style>

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

@ -3,32 +3,79 @@
<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">
@ -36,10 +83,26 @@
<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">
@ -63,8 +126,16 @@
<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>
@ -74,18 +145,46 @@
<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">
@ -99,7 +198,9 @@
</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>
@ -135,17 +236,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' },
],
}
})
@ -163,26 +264,28 @@ 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
})
}
@ -198,9 +301,11 @@ 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,8 +1,28 @@
<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')">
@ -10,18 +30,39 @@
</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>
@ -47,7 +88,7 @@ const initialFormData = {
brand_name: '',
logo: '',
desc: '',
sort: ''
sort: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -57,8 +98,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' },
],
}
})
@ -78,11 +119,13 @@ 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
})
}
@ -109,13 +152,19 @@ 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,13 +1,44 @@
<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')">
@ -15,14 +46,24 @@
</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>
@ -32,7 +73,12 @@
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)
@ -49,7 +95,7 @@ const initialFormData = {
is_show: 1,
child_count: 0,
// sort: 9999,
level: 1
level: 1,
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -59,14 +105,16 @@ 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' }],
}
})
@ -91,11 +139,13 @@ 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
})
}
@ -105,9 +155,11 @@ 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
)
})
}
@ -132,7 +184,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,26 +3,59 @@
<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>
@ -43,24 +76,39 @@
</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>
@ -83,16 +131,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'])
@ -103,7 +151,7 @@ const couponIds: any = computed({
},
set(value) {
emit('update:modelValue', value)
}
},
})
const showDialog = ref(false)
@ -126,14 +174,13 @@ const couponTable = reactive({
title: '',
status: 1,
verify_coupon_ids: '',
}
},
})
const searchFormRef = ref()
//
const initData = () => {
}
const initData = () => {}
initData()
@ -181,36 +228,37 @@ 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) => {
@ -222,28 +270,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]
}
}
}
@ -254,9 +302,9 @@ const show = () => {
//
const clear = () => {
for (let k in selectCoupon) {
delete selectCoupon[k];
delete selectCoupon[k]
}
setCouponSelected();
setCouponSelected()
}
const save = () => {
@ -264,21 +312,26 @@ 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)
@ -289,7 +342,7 @@ const save = () => {
defineExpose({
showDialog,
selectCoupon,
selectCouponNum
selectCouponNum,
})
</script>

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

@ -1,23 +1,59 @@
<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>
@ -26,21 +62,33 @@
<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>
@ -70,7 +118,7 @@ const initialFormData = {
image_arr: '',
is_anonymous: 1,
scores: 5,
create_time: ''
create_time: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -81,23 +129,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' },
],
}
})
@ -118,11 +166,13 @@ 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
})
}
@ -143,7 +193,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,12 +1,30 @@
<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>
@ -15,58 +33,157 @@
<!-- 商品标签 -->
<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>
<!-- 是否赠品 -->
@ -76,7 +193,9 @@
<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>
<!-- 配送设置 -->
@ -84,35 +203,105 @@
<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>
@ -125,18 +314,48 @@
</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>
@ -146,7 +365,12 @@
<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>
@ -164,11 +388,14 @@ 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)
@ -195,7 +422,7 @@ const initialFormData = {
stock_type: 'inc',
is_free_shipping: 1,
is_gift: 0,
stock: ''
stock: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -204,7 +431,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({
@ -223,21 +450,29 @@ 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)) {
@ -253,15 +488,19 @@ 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 {
@ -273,16 +512,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
}
@ -310,13 +549,14 @@ 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: [],
@ -331,11 +571,12 @@ 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
})
}
@ -349,7 +590,7 @@ const handleMenuSelect = (index: string) => {
const setTypeList = reactive([])
const getGoodsTypeList = () => {
getGoodsBatchSetDict().then((res) => {
Object.assign(setTypeList, res.data);
Object.assign(setTypeList, res.data)
})
}
getGoodsTypeList()
@ -360,7 +601,7 @@ const labelOptions = reactive([])
//
const toGoodsLabelEvent = () => {
const url = router.resolve({
path: '/shop/goods/label'
path: '/shop/goods/label',
})
window.open(url.href)
}
@ -374,7 +615,7 @@ const refreshGoodsLabel = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success'
type: 'success',
})
}
}
@ -389,7 +630,7 @@ const serviceOptions = reactive([])
//
const toGoodsServiceEvent = () => {
const url = router.resolve({
path: '/shop/goods/service'
path: '/shop/goods/service',
})
window.open(url.href)
}
@ -403,7 +644,7 @@ const refreshGoodsService = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success'
type: 'success',
})
}
}
@ -417,7 +658,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())
@ -426,7 +667,7 @@ const categoryHandleChange = (value: any) => {
//
const toGoodsCategoryEvent = () => {
const url = router.resolve({
path: '/shop/goods/category'
path: '/shop/goods/category',
})
window.open(url.href)
}
@ -443,21 +684,25 @@ 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',
})
}
}
@ -474,7 +719,7 @@ const brandOptions = reactive([])
//
const toGoodsBrandEvent = () => {
const url = router.resolve({
path: '/shop/goods/brand'
path: '/shop/goods/brand',
})
window.open(url.href)
}
@ -488,7 +733,7 @@ const refreshGoodsBrand = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success'
type: 'success',
})
}
}
@ -504,7 +749,7 @@ const posterOptions = reactive([])
//
const toPosterEvent = () => {
const url = router.resolve({
path: '/poster/list'
path: '/poster/list',
})
window.open(url.href)
}
@ -512,7 +757,7 @@ const toPosterEvent = () => {
//
const refreshGoodsPoster = (bool = false) => {
getPosterList({
type: 'shop_goods'
type: 'shop_goods',
}).then((res) => {
const data = res.data
if (data) {
@ -520,7 +765,7 @@ const refreshGoodsPoster = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success'
type: 'success',
})
}
}
@ -530,7 +775,6 @@ const refreshGoodsPoster = (bool = false) => {
refreshGoodsPoster()
/** *****************商品海报-end *************************/
//
const deliveryTypeCheckBox = reactive([])
@ -552,7 +796,7 @@ getShopDeliveryList().then((res) => {
//
const toDeliveryTemplateEvent = () => {
const url = router.resolve({
path: '/shop/order/shipping/template'
path: '/shop/order/shipping/template',
})
window.open(url.href)
}
@ -566,7 +810,7 @@ const refreshDeliveryTemplate = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success'
type: 'success',
})
}
}
@ -581,7 +825,7 @@ const diyFormOptions = reactive([])
//
const toDiyFormEvent = () => {
const url = router.resolve({
path: '/diy_form/list'
path: '/diy_form/list',
})
window.open(url.href)
}
@ -590,7 +834,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) {
@ -598,7 +842,7 @@ const refreshDiyForm = (bool = false) => {
if (bool) {
ElMessage({
message: t('refreshSuccess'),
type: 'success'
type: 'success',
})
}
}
@ -610,7 +854,7 @@ refreshDiyForm()
defineExpose({
showDialog,
show
show,
})
</script>

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

@ -1,8 +1,14 @@
<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>
@ -10,14 +16,16 @@
<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>
@ -60,7 +68,11 @@ 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
})
}
@ -78,7 +90,7 @@ const copyEvent = (text: string) => {
if (!isSupported.value) {
ElMessage({
message: t('notSupportCopy'),
type: 'warning'
type: 'warning',
})
}
copy(text)
@ -88,14 +100,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,7 +1,13 @@
<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>
@ -10,21 +16,50 @@
<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>
@ -33,21 +68,60 @@
</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>
@ -56,17 +130,34 @@
</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>
@ -89,12 +180,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)
@ -105,7 +196,7 @@ const emit = defineEmits(['load'])
const tableData = reactive({
loading: true,
data: [],
member_level: []
member_level: [],
})
/**
* 获取商品列表
@ -114,8 +205,9 @@ 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) => {
@ -126,18 +218,25 @@ 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
})
}
@ -155,14 +254,23 @@ 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 += '折'
@ -190,7 +298,10 @@ 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
@ -207,7 +318,7 @@ const batchGoodsBtn = (level_id:any) => {
if (!multipleSelection.value.length) {
ElMessage({
message: '请选择要操作的商品',
type: 'warning'
type: 'warning',
})
return false
}
@ -225,7 +336,11 @@ 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 = ''
@ -243,14 +358,22 @@ 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}]的指定价格不能大于商品原价`
)
}
}
})
@ -266,8 +389,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
@ -276,7 +399,7 @@ const save = () => {
defineExpose({
showDialog,
show
show,
})
</script>

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

@ -1,21 +1,44 @@
<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>
@ -24,41 +47,96 @@
<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>
@ -80,7 +158,7 @@ import { ElMessage } from 'element-plus'
import {
getActiveGoodsCount,
getGoodsSkuList,
editGoodsListPrice
editGoodsListPrice,
} from '@/addon/shop/api/goods'
const goods: any = reactive({})
@ -92,12 +170,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) => {
@ -114,7 +192,7 @@ const saveBatch = () => {
if (batchOperation.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t(batchOperation.field + 'Placeholder')}`
message: `${t(batchOperation.field + 'Placeholder')}`,
})
return
}
@ -122,7 +200,7 @@ const saveBatch = () => {
if (!regExp.digit.test(batchOperation.value)) {
ElMessage({
type: 'warning',
message: `${t(batchOperation.field + 'Tips')}`
message: `${t(batchOperation.field + 'Tips')}`,
})
return
}
@ -130,7 +208,7 @@ const saveBatch = () => {
if (batchOperation.value < 0) {
ElMessage({
type: 'warning',
message: `${t(batchOperation.field + 'NotZeroTips')}`
message: `${t(batchOperation.field + 'NotZeroTips')}`,
})
return
}
@ -149,33 +227,35 @@ 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 = () => {
@ -187,14 +267,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
}
@ -203,14 +283,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
}
@ -219,14 +299,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
}
@ -243,14 +323,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
})
}
@ -258,7 +338,7 @@ const save = () => {
defineExpose({
showDialog,
show
show,
})
</script>

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

@ -3,38 +3,90 @@
<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>
@ -42,37 +94,87 @@
<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>
@ -81,25 +183,57 @@
<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>
@ -108,7 +242,10 @@
</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>
@ -116,16 +253,30 @@
<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>
@ -144,42 +295,47 @@ 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() {
@ -187,7 +343,7 @@ const goodsIds: any = computed({
},
set(value) {
emit('update:modelValue', value)
}
},
})
const showDialog = ref(false)
@ -217,11 +373,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()
@ -249,17 +405,21 @@ 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
)
}
})
@ -283,18 +443,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') {
@ -302,13 +462,15 @@ 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 {
@ -329,11 +491,10 @@ 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)
@ -346,18 +507,19 @@ 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]
});
})
}
}
@ -366,52 +528,61 @@ 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)
//
@ -421,22 +592,22 @@ const subChildHandleCheckAllChange = (selected: any,parentData: any,data: any)=
//
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
}
}
@ -444,13 +615,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>[]
@ -459,79 +630,85 @@ 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) => {
@ -543,31 +720,30 @@ 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]
}
}
}
@ -578,49 +754,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 = () => {
@ -628,64 +804,72 @@ 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,9 +1,15 @@
<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>
@ -11,14 +17,16 @@
<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>
@ -65,7 +73,11 @@ 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
})
}
@ -88,7 +100,7 @@ const copyEvent = (text: string) => {
if (!isSupported.value) {
ElMessage({
message: t('notSupportCopy'),
type: 'warning'
type: 'warning',
})
}
copy(text)
@ -98,14 +110,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,44 +1,94 @@
<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>
@ -59,7 +109,7 @@ import { ElMessage } from 'element-plus'
import {
getActiveGoodsCount,
getGoodsSkuList,
editGoodsListStock
editGoodsListStock,
} from '@/addon/shop/api/goods'
const goods: any = reactive({})
@ -75,18 +125,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
}
@ -94,7 +144,7 @@ const saveBatch = () => {
if (!regExp.number.test(batchOperation.value)) {
ElMessage({
type: 'warning',
message: `${t('stockTips')}`
message: `${t('stockTips')}`,
})
return
}
@ -102,7 +152,7 @@ const saveBatch = () => {
if (batchOperation.value < 0) {
ElMessage({
type: 'warning',
message: `${t('stockNotZeroTips')}`
message: `${t('stockNotZeroTips')}`,
})
return
}
@ -119,11 +169,13 @@ 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
})
}
@ -137,15 +189,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 = () => {
@ -157,14 +209,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) {
@ -186,13 +238,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
})
@ -201,7 +253,7 @@ const save = () => {
defineExpose({
showDialog,
show
show,
})
</script>

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

@ -1,12 +1,41 @@
<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')">
@ -15,16 +44,34 @@
<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>
@ -34,21 +81,46 @@
</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>
@ -64,7 +136,7 @@ import { addLabel, editLabel, getLabelInfo } from '@/addon/shop/api/goods'
const prop = defineProps({
groupList: {
type: Object,
default: []
default: [],
},
})
@ -88,7 +160,7 @@ const initialFormData = {
icon: '',
memo: '',
sort: '',
status: 1
status: 1,
}
const predefineColors = ref([
@ -109,7 +181,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 })
@ -120,11 +192,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' },
],
}
})
@ -144,11 +216,13 @@ 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
})
}
@ -175,7 +249,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,18 +1,51 @@
<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>
@ -23,7 +56,11 @@ 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)
@ -35,7 +72,7 @@ const title = ref('')
const initialFormData = {
group_id: '',
group_name: '',
sort: ''
sort: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -45,8 +82,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' },
],
}
})
@ -66,11 +103,13 @@ 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
})
}
@ -97,7 +136,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,38 +3,90 @@
<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>
@ -42,63 +94,147 @@
<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>
<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>
<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>
@ -107,25 +243,41 @@
</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>
@ -145,39 +297,41 @@ 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() {
@ -185,7 +339,7 @@ const goodsIds: any = computed({
},
set(value) {
emit('update:modelValue', value)
}
},
})
const showDialog = ref(false)
@ -214,8 +368,8 @@ const goodsTable = reactive({
goods_ids: '',
verify_goods_ids: '',
verify_sku_ids: '',
goods_type: ''
}
goods_type: '',
},
})
const searchFormRef = ref()
@ -244,17 +398,21 @@ 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
)
}
})
@ -276,21 +434,20 @@ 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') {
@ -298,13 +455,15 @@ 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 {
@ -325,11 +484,10 @@ 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) {
@ -343,18 +501,19 @@ 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]
});
})
}
}
@ -364,53 +523,61 @@ 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)
//
@ -418,25 +585,24 @@ const subChildHandleCheckAllChange = (selected: any,parentData: any,data: any)=
}
}
//
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
}
}
@ -444,13 +610,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>[]
@ -459,79 +625,85 @@ 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) => {
@ -543,19 +715,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) => {
//
@ -564,9 +736,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]
}
}
}
@ -577,49 +749,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 = () => {
@ -627,64 +799,72 @@ 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,21 +1,55 @@
<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>
@ -38,7 +72,7 @@ const initialFormData = {
service_id: '',
service_name: '',
image: '',
desc: ''
desc: '',
}
const formData: Record<string, any> = reactive({ ...initialFormData })
@ -48,8 +82,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' },
],
}
})
@ -69,11 +103,13 @@ 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
})
}
@ -100,7 +136,7 @@ const setFormData = async (row: any = null) => {
defineExpose({
showDialog,
setFormData
setFormData,
})
</script>

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

@ -1,7 +1,6 @@
<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">
@ -9,20 +8,40 @@
</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>
@ -30,15 +49,28 @@
<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>
@ -50,19 +82,45 @@
<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>
@ -71,43 +129,119 @@
<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>
@ -117,7 +251,15 @@
<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'
@ -133,8 +275,8 @@ const evaluateTable = reactive({
loading: true,
data: [],
searchParam: {
goods_name: ''
}
goods_name: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -149,8 +291,9 @@ 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
@ -160,7 +303,8 @@ const loadEvaluateList = (page: number = 1) => {
})
return item
})
}).catch(() => {
})
.catch(() => {
evaluateTable.loading = false
})
}
@ -179,33 +323,31 @@ 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(() => {})
})
}
@ -233,7 +375,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>()
@ -245,8 +387,12 @@ 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) => {
@ -254,10 +400,12 @@ 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
})
}
@ -280,4 +428,5 @@ 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,7 +1,6 @@
<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">
@ -11,50 +10,105 @@
<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>
@ -62,7 +116,11 @@
<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'
@ -85,8 +143,8 @@ const labelTable = reactive({
searchParam: {
group_name: '',
order: '',
sort: ''
}
sort: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -116,12 +174,14 @@ 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
})
}
@ -150,17 +210,16 @@ 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(() => {})
})
}
@ -169,7 +228,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !/^\d{0,8}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${ t('sortTips') }`
message: `${t('sortTips')}`,
})
return
}
@ -178,9 +237,8 @@ const sortInputListener = debounce((sort, row) => {
}
modifyLabelGroupSort({
group_id: row.group_id,
sort
}).then((res) => {
})
sort,
}).then((res) => {})
})
const resetForm = (formEl: FormInstance | undefined) => {
@ -190,5 +248,4 @@ 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,7 +1,6 @@
<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">
@ -11,82 +10,186 @@
<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>
@ -94,7 +197,13 @@
<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'
@ -118,11 +227,11 @@ const labelTable = reactive({
label_name: '',
group_id: '',
order: '',
sort: ''
}
sort: '',
},
})
const groupList: any = reactive([]);
const groupList: any = reactive([])
const searchFormRef = ref<FormInstance>()
@ -151,12 +260,14 @@ 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
})
}
@ -171,7 +282,7 @@ const initData = ()=>{
loadLabelList()
}
initData();
initData()
const editLabelDialog: Record<string, any> | null = ref(null)
@ -196,17 +307,16 @@ 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(() => {})
})
}
@ -215,7 +325,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !/^\d{0,8}$/.test(sort)) {
ElMessage({
type: 'warning',
message: `${ t('sortTips') }`
message: `${t('sortTips')}`,
})
return
}
@ -224,11 +334,9 @@ const sortInputListener = debounce((sort, row) => {
}
modifyLabelSort({
label_id: row.label_id,
sort
}).then((res) => {
sort,
}).then((res) => {})
})
})
const isRepeat = ref(false)
@ -239,7 +347,7 @@ const modifyLabelStatusEvent = (label_id: any, status: any) => {
modifyLabelStatus({
label_id,
status
status,
}).then((res) => {
loadLabelList()
isRepeat.value = false
@ -253,5 +361,4 @@ 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,106 +1,257 @@
<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 />
@ -109,9 +260,17 @@
</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 />
@ -119,37 +278,87 @@
</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">
@ -159,20 +368,29 @@
<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" />
@ -181,14 +399,23 @@
<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'
@ -197,7 +424,17 @@ 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()
@ -223,8 +460,8 @@ const goodsTable = reactive({
end_price: '',
status: route.query.status || '1',
order: '',
sort: ''
}
sort: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -232,7 +469,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})$/,
}
//
@ -247,14 +484,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 = () => {
@ -267,29 +504,33 @@ 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
)
}
})
@ -350,13 +591,20 @@ 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
}
}
@ -366,8 +614,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)
}
@ -392,21 +640,19 @@ 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()
})
@ -419,7 +665,7 @@ const batchGoodsStatus = (status: any) => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`
message: `${t('batchEmptySelectedGoodsTips')}`,
})
return
}
@ -431,7 +677,7 @@ const batchGoodsStatus = (status: any) => {
editGoodsStatus({
goods_ids: goodsIds,
status
status,
}).then((res) => {
loadGoodsList()
})
@ -442,7 +688,7 @@ const batchSetGoods = () => {
if (multipleSelection.value.length == 0) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`
message: `${t('batchEmptySelectedGoodsTips')}`,
})
return
}
@ -450,23 +696,20 @@ 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
@ -476,11 +719,13 @@ const batchDeleteGoods = () => {
})
deleteGoods({
goods_ids: goodsIds
}).then(() => {
goods_ids: goodsIds,
})
.then(() => {
loadGoodsList()
repeat.value = false
}).catch(() => {
})
.catch(() => {
repeat.value = false
})
})
@ -491,7 +736,7 @@ const sortInputListener = debounce((sort, row) => {
if (isNaN(sort) || !regExp.number.test(sort)) {
ElMessage({
type: 'warning',
message: `${t('sortTips')}`
message: `${t('sortTips')}`,
})
return
}
@ -500,7 +745,7 @@ const sortInputListener = debounce((sort, row) => {
}
editGoodsSort({
goods_id: row.goods_id,
sort
sort,
}).then((res) => {
// loadGoodsList();
})
@ -510,45 +755,63 @@ 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
}
@ -560,14 +823,16 @@ 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
})
}
@ -623,7 +888,7 @@ const spreadEvent = (data: any) => {
//
const memberLevel = ref([])
const getMemberLevelAllFn = () => {
getMemberLevelAll().then(res => {
getMemberLevelAll().then((res) => {
memberLevel.value = res.data ? res.data : []
})
}
@ -637,24 +902,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
})
})
@ -662,21 +927,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
})
})
@ -710,7 +975,8 @@ 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,67 +1,139 @@
<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>
@ -69,18 +141,29 @@
</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">
@ -88,12 +171,16 @@
<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>
@ -110,7 +197,7 @@ import {
getRecycleGoodsPageList,
getCategoryTree,
getGoodsType,
recycleGoods
recycleGoods,
} from '@/addon/shop/api/goods'
const route = useRoute()
@ -128,8 +215,8 @@ const goodsTable = reactive({
goods_category: [],
goods_type: '',
order: '',
sort: ''
}
sort: '',
},
})
const searchFormRef = ref()
@ -153,17 +240,21 @@ 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
)
}
})
//
@ -201,13 +292,20 @@ 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
}
}
@ -217,18 +315,16 @@ 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
@ -238,12 +334,14 @@ const batchRecycle = () => {
})
recycleGoods({
goods_ids: goodsIds
}).then(() => {
goods_ids: goodsIds,
})
.then(() => {
loadGoodsList()
toggleCheckbox.value = false
repeat.value = false
}).catch(() => {
})
.catch(() => {
repeat.value = false
})
})
@ -261,12 +359,14 @@ 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
})
}
@ -275,24 +375,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
})
})
@ -319,7 +419,6 @@ const sortChange = (event: any) => {
}
loadGoodsList()
}
</script>
<style lang="scss" scoped>

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

@ -1,7 +1,6 @@
<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">
@ -9,25 +8,47 @@
</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]">-->
@ -42,18 +63,34 @@
<!-- </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>
@ -80,8 +117,8 @@ const serveTable = reactive({
loading: true,
data: [],
searchParam: {
service_name: ''
}
service_name: '',
},
})
const searchFormRef = ref<FormInstance>()
@ -96,12 +133,14 @@ 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
})
}
@ -130,17 +169,16 @@ 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(() => {})
})
}
@ -151,5 +189,4 @@ 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,7 +3,9 @@
<!-- 实时概况 -->
<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>
@ -15,7 +17,12 @@
<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>
@ -29,7 +36,10 @@
<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>
@ -40,7 +50,12 @@
<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>
@ -54,7 +69,10 @@
<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>
@ -65,7 +83,12 @@
<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>
@ -79,7 +102,10 @@
<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>
@ -90,7 +116,12 @@
<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>
@ -104,7 +135,10 @@
<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>
@ -118,13 +152,24 @@
<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>
@ -134,38 +179,74 @@
</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>
@ -180,7 +261,10 @@
<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">
@ -205,7 +289,7 @@ import {
getShopYesterdayCountList,
getShopStat,
getShopOrderStat,
getShopGoodsStat
getShopGoodsStat,
} from '@/addon/shop/api/shop'
import * as echarts from 'echarts'
import { useRouter } from 'vue-router'
@ -215,36 +299,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>([])
@ -258,7 +342,6 @@ 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
@ -284,18 +367,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
@ -312,18 +395,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,22 +1,40 @@
<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>
@ -32,14 +50,27 @@
<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>
@ -55,7 +86,12 @@
<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>
@ -71,7 +107,12 @@
<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>
@ -92,36 +133,64 @@
<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>
@ -129,21 +198,33 @@
</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>
@ -208,7 +289,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>()
@ -217,41 +298,41 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' }
],
price: [
{ required: true, validator: priceRule, trigger: 'blur' }
],
goods_ids: [
{ required: true, message: t('请选择商品'), trigger: 'blur' }
{ required: true, message: t('titlePlaceholder'), 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' }
],
valid_time:[
{ required: true, validator: validTime, trigger: 'blur' }
],
receive_time:[
{ required: true, validator: receiveTime, trigger: 'blur' }
],
remain_count: [
{ required: true, validator: remainCount, 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' }
]
{ 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('领取结束时间不能小于等于领取开始时间')))
}
}
@ -259,11 +340,21 @@ 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('有效期不能小于等于领取结束时间')))
}
}
@ -271,33 +362,54 @@ 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()
@ -305,8 +417,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()
}
//
@ -323,20 +435,22 @@ 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
})
}
@ -346,7 +460,6 @@ const onSave = async (formEl: FormInstance | undefined) => {
const back = () => {
router.push('/shop/marketing/coupon/list')
}
</script>
<style lang="scss">
@ -360,7 +473,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,11 +1,23 @@
<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>
@ -15,7 +27,13 @@
</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>
@ -25,7 +43,13 @@
</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>
@ -36,20 +60,40 @@
</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>
@ -58,12 +102,19 @@
<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')" />
@ -79,19 +130,29 @@
</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>
@ -107,7 +168,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()
@ -117,7 +178,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')
@ -131,8 +192,8 @@ const tableData = reactive({
loading: false,
data: [],
searchParam: {
keywords: ''
}
keywords: '',
},
})
const searchFormRef = ref<FormInstance>()
const couponStatistics = ref([])
@ -145,28 +206,32 @@ 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)
}
@ -177,15 +242,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,9 +1,15 @@
<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>
@ -11,14 +17,16 @@
<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>
@ -65,7 +73,11 @@ 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
})
}
@ -83,7 +95,7 @@ const copyEvent = (text: string) => {
if (!isSupported.value) {
ElMessage({
message: t('notSupportCopy'),
type: 'warning'
type: 'warning',
})
}
copy(text)
@ -93,14 +105,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,22 +1,45 @@
<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>
<!-- 优惠券类型 -->
@ -30,14 +53,26 @@
<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>
@ -53,7 +88,12 @@
<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>
@ -69,7 +109,12 @@
<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>
@ -77,7 +122,11 @@
<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>
@ -90,58 +139,98 @@
<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>
@ -152,7 +241,11 @@
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'
@ -187,7 +280,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>()
@ -196,41 +289,41 @@ const formRef = ref<FormInstance>()
const formRules = computed(() => {
return {
title: [
{ required: true, message: t('titlePlaceholder'), trigger: 'blur' }
],
price: [
{ required: true, validator: priceRule, trigger: 'blur' }
],
goods_ids: [
{ required: true, message: t('请选择商品'), trigger: 'blur' }
{ required: true, message: t('titlePlaceholder'), 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' }
],
valid_time: [
{ required: true, validator: validTime, trigger: 'blur' }
],
receive_time: [
{ required: true, validator: receiveTime, trigger: 'blur' }
],
remain_count: [
{ required: true, validator: remainCount, 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' }
]
{ 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('领取结束时间不能小于等于领取开始时间')))
}
}
@ -238,11 +331,21 @@ 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('有效期不能小于等于领取结束时间')))
}
}
@ -250,33 +353,54 @@ 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()
@ -305,10 +429,12 @@ 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
})
}
@ -328,22 +454,28 @@ 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
})
}
@ -385,7 +517,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,7 +2,6 @@
<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">
@ -11,26 +10,53 @@
</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>
@ -52,13 +78,17 @@
<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">
@ -70,8 +100,12 @@
<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>
@ -79,7 +113,12 @@
<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>
@ -88,21 +127,57 @@
<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>
@ -115,11 +190,16 @@
<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()
@ -136,13 +216,13 @@ const tableData = reactive({
searchParam: {
title: '',
status: '',
}
},
})
const searchFormRef = ref<FormInstance>()
const getCouponStatusListFn = () => {
getCouponStatusList().then(res=>{
getCouponStatusList().then((res) => {
statusList.value = res.data
})
}
@ -154,17 +234,23 @@ 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)
@ -186,42 +272,40 @@ 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(() => {})
})
}
@ -230,7 +314,6 @@ 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,62 +1,123 @@
<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>
@ -69,110 +130,230 @@
<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>
@ -182,15 +363,30 @@
<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>
@ -202,7 +398,9 @@
<!-- 提交按钮 -->
<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>
@ -213,7 +411,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'
@ -248,43 +446,54 @@ 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()
@ -293,7 +502,10 @@ 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
@ -309,7 +521,10 @@ 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
@ -326,19 +541,23 @@ 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
@ -357,24 +576,25 @@ 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>({
@ -382,12 +602,13 @@ 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 = ''
@ -404,13 +625,14 @@ 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 = ''
@ -423,7 +645,8 @@ const goodsSelect = (value: any) => {
el.index = index
el.valid = false
return el
})
}
)
}
setGoodsList()
if (formRef.value) formRef.value.validateField('goods_list').catch(() => {})
@ -460,11 +683,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
}
//
@ -492,13 +715,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: '',
})
//
@ -522,7 +745,10 @@ 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
@ -537,7 +763,7 @@ const saveBatch = () => {
if (!multipleSelection.value.length) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`
message: `${t('batchEmptySelectedGoodsTips')}`,
})
return
}
@ -545,25 +771,28 @@ 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
}
@ -571,68 +800,85 @@ 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 {
@ -642,28 +888,47 @@ 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)
@ -686,40 +951,63 @@ 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
@ -736,7 +1024,6 @@ const enabledEvent = (row: any) => {
row.reduce_money = '0'
row.specify_price = row.goodsSku.price + ''
}
}
const back = () => {
router.push('/shop/marketing/discount/list')
@ -754,7 +1041,7 @@ const back = () => {
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,5 +1,11 @@
<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" />
@ -8,7 +14,14 @@
<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">
@ -81,31 +94,67 @@
<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>
@ -115,26 +164,58 @@
<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>
@ -143,43 +224,83 @@
</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">
@ -187,47 +308,107 @@
<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>
@ -237,7 +418,12 @@
<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'
@ -256,8 +442,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) => {
@ -277,8 +463,8 @@ const goodsParams = reactive({
data: [],
searchParam: {
keyword: '',
active_id: id
}
active_id: id,
},
})
const getActiveDiscountGoodsPageListFn = (page: number = 1) => {
goodsParams.loading = true
@ -286,12 +472,14 @@ 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
})
}
@ -300,8 +488,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)
}
@ -318,8 +506,8 @@ const orderParams = reactive({
status: '',
create_time: [],
pay_time: [],
active_id: ''
}
active_id: '',
},
})
const getActiveDiscountOrderPageListFn = (page: number = 1) => {
@ -328,19 +516,21 @@ 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()
}
//
@ -351,8 +541,8 @@ const memberParams = reactive({
loading: false,
data: [],
searchParam: {
active_id: id
}
active_id: id,
},
})
const getActiveDiscountMemberPageListFn = (page: number = 1) => {
memberParams.loading = true
@ -360,12 +550,14 @@ 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
})
}
@ -373,15 +565,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()
@ -392,16 +584,15 @@ 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,27 +1,60 @@
<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>
@ -30,7 +63,12 @@
<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) {
@ -42,18 +80,32 @@
} 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) {
@ -65,18 +117,32 @@
} 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) {
@ -88,66 +154,118 @@
} 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>()
@ -155,7 +273,7 @@
//
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) => {
@ -165,16 +283,16 @@
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>({
@ -182,7 +300,7 @@
limit: 5,
total: 0,
data: [],
list: []
list: [],
})
const setGoodsList = (page = 1) => {
@ -207,22 +325,22 @@
//
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: '',
})
//
@ -246,7 +364,10 @@
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
@ -260,7 +381,7 @@
if (!multipleSelection.value.length) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`
message: `${t('batchEmptySelectedGoodsTips')}`,
})
return
}
@ -268,25 +389,28 @@
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
}
@ -294,68 +418,78 @@
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) {
@ -382,20 +516,20 @@
//
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) {
@ -420,7 +554,6 @@
row.reduce_money = '0'
row.specify_price = row.price + ''
}
}
const validFn = (row: any) => {
if (row.is_enabled === 0) {
@ -428,7 +561,10 @@
}
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
@ -444,7 +580,10 @@
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
@ -459,36 +598,42 @@
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>
@ -502,7 +647,7 @@
margin: 0;
}
input[type="number"] {
input[type='number'] {
-webkit-appearance: textfield;
-moz-appearance: textfield;
-o-appearance: textfield;
@ -519,4 +664,3 @@
overflow: initial !important;
}
</style>

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

@ -1,20 +1,31 @@
<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>
@ -23,9 +34,22 @@
<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) => {
@ -33,11 +57,17 @@
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) => {
@ -45,14 +75,24 @@
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>
@ -62,7 +102,9 @@
<!-- 提交按钮 -->
<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>
@ -72,7 +114,10 @@
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'
@ -84,21 +129,20 @@ 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 = () => {
@ -112,10 +156,12 @@ 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,12 +1,22 @@
<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>
@ -77,7 +87,11 @@
</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" />
@ -86,31 +100,68 @@
<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>
@ -120,26 +171,57 @@
<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>
@ -148,38 +230,74 @@
</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">
@ -187,47 +305,106 @@
<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>
@ -240,7 +417,12 @@ 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()
@ -278,8 +460,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
@ -287,12 +469,14 @@ 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
})
}
@ -302,8 +486,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)
}
@ -320,8 +504,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
@ -329,12 +513,14 @@ 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
})
}
@ -352,8 +538,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
@ -361,12 +547,14 @@ 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
})
}
@ -375,7 +563,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,64 +1,124 @@
<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>
@ -72,111 +132,227 @@
<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>
@ -186,14 +362,33 @@
<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>
@ -205,7 +400,9 @@
<!-- 提交按钮 -->
<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>
@ -216,7 +413,10 @@
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'
@ -249,40 +449,51 @@ 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('活动结束时间不能小于等于活动开始时间')))
}
@ -301,7 +512,6 @@ const getActiveDiscountInfoFn = (id: number) => {
formData.value.goods_ids.push(el.goods_id)
formData.value.goods_list.push(el)
})
setGoodsList()
}
@ -311,7 +521,10 @@ 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
@ -327,7 +540,10 @@ 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
@ -343,19 +559,23 @@ 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
@ -374,24 +594,25 @@ 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>({
@ -399,12 +620,13 @@ 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 = ''
@ -421,13 +643,14 @@ 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 = ''
@ -440,7 +663,8 @@ const goodsSelect = (value: any) => {
el.index = index
el.valid = false
return el
})
}
)
}
setGoodsList()
if (formRef.value) formRef.value.validateField('goods_list').catch(() => {})
@ -474,11 +698,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) => {
@ -503,13 +727,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: '',
})
//
@ -533,7 +757,10 @@ 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
@ -547,7 +774,7 @@ const saveBatch = () => {
if (!multipleSelection.value.length) {
ElMessage({
type: 'warning',
message: `${t('batchEmptySelectedGoodsTips')}`
message: `${t('batchEmptySelectedGoodsTips')}`,
})
return
}
@ -555,25 +782,28 @@ 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
}
@ -581,68 +811,85 @@ 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 {
@ -652,28 +899,47 @@ 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)
@ -695,39 +961,63 @@ 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')
@ -745,7 +1035,7 @@ const back = () => {
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,7 +1,6 @@
<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">
@ -10,36 +9,88 @@
</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>
@ -48,20 +99,56 @@
</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>
@ -73,10 +160,15 @@
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()
@ -91,8 +183,8 @@ const tableData = reactive({
data: [],
searchParam: {
active_name: '',
active_status:''
}
active_status: '',
},
})
const searchFormRef = ref<FormInstance>()
const loadDiscountList = (page: number = 1) => {
@ -102,17 +194,23 @@ 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()
@ -121,7 +219,7 @@ const resetForm = (formEl: FormInstance | undefined) => {
//
const activeStatusOption = ref({})
const getActiveDiscountStatusListFn = () => {
getActiveDiscountStatusList().then(res=>{
getActiveDiscountStatusList().then((res) => {
activeStatusOption.value = res.data
})
}
@ -133,9 +231,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) => {
@ -143,32 +241,30 @@ 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,81 +1,146 @@
<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,
@ -86,19 +151,19 @@
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
@ -107,8 +172,9 @@
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
@ -116,55 +182,54 @@
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,34 +1,73 @@
<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>
@ -36,29 +75,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 + ''
@ -70,32 +109,30 @@
})
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,7 +1,6 @@
<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">
@ -10,29 +9,63 @@
</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>
@ -40,18 +73,35 @@
<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>
@ -67,27 +117,67 @@
<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>
@ -104,7 +194,12 @@ 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()
@ -120,8 +215,8 @@ const tableData = reactive({
searchParam: {
names: '',
status: '',
create_time: []
}
create_time: [],
},
})
const searchFormRef = ref<FormInstance>()
const loadExchangeGoodsList = (page: number = 1) => {
@ -131,21 +226,27 @@ 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
})
}
@ -167,32 +268,30 @@ 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,43 +1,89 @@
<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>
@ -50,7 +96,11 @@
<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>
@ -64,8 +114,18 @@
<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>
@ -79,10 +139,14 @@
<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>
@ -90,7 +154,9 @@
<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>
@ -105,19 +171,30 @@
<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>
@ -125,7 +202,12 @@
<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'
@ -152,7 +234,7 @@ interface OrderTable {
total: number
loading: boolean
data: any[]
searchParam: any,
searchParam: any
}
const orderTable = reactive<OrderTable>({
page: 1,
@ -168,8 +250,8 @@ const orderTable = reactive<OrderTable>({
status: '',
create_time: [],
pay_time: [],
activity_type:'exchange'
}
activity_type: 'exchange',
},
})
const searchFormRef = ref<FormInstance>()
@ -184,8 +266,9 @@ 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) => {
@ -194,12 +277,17 @@ 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,40 +10,103 @@
</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>
@ -66,28 +129,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)
@ -108,7 +171,7 @@ const rankTable = reactive({
searchParam: {
name: '',
verify_rank_ids: '',
}
},
})
const searchFormRef = ref()
@ -122,24 +185,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]
}
}
}
@ -147,19 +210,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]
})
}
}
}
@ -167,14 +230,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)
}
}
});
})
}
/**
@ -184,36 +247,37 @@ 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) => {
//
@ -221,13 +285,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]
}
}
}
@ -238,28 +302,33 @@ 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)
@ -269,7 +338,7 @@ const save = () => {
defineExpose({
showDialog,
selectRank,
selectRankNum
selectRankNum,
})
</script>
@ -283,4 +352,3 @@ defineExpose({
}
}
</style>

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

@ -6,14 +6,24 @@
<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) => {
@ -21,8 +31,10 @@
callback(t('imagePlaceholder'))
}
callback()
}
}]">
},
},
]"
>
<upload-image v-model="formData.rank_images" :limit="1" />
</el-form-item>
<el-form-item :label="t('noColor')">
@ -32,9 +44,19 @@
<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">
@ -48,33 +70,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 {
@ -84,22 +106,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()
/**
* 使用默认说明
@ -109,23 +131,25 @@ 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,61 +1,148 @@
<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>
@ -65,7 +152,12 @@
<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>
@ -73,174 +165,269 @@
</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: [
@ -255,33 +442,32 @@ 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 {
@ -291,143 +477,142 @@ 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) {
@ -437,19 +622,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) {
@ -458,54 +643,57 @@ 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,18 +3,34 @@
<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>
@ -22,29 +38,75 @@
<!-- 列表 -->
<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>
@ -52,24 +114,53 @@
</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>
@ -77,18 +168,29 @@
</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({
@ -98,48 +200,54 @@ 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
@ -152,16 +260,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) => {
@ -179,101 +287,102 @@ 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,30 +1,42 @@
<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>
@ -54,7 +66,7 @@ interface detailType{
appList: appListType[]
}
const detail = reactive<detailType>({
appList: []
appList: [],
})
const getAppList = async () => {
@ -70,11 +82,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,5 +1,11 @@
<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" />
@ -8,7 +14,14 @@
<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">
@ -96,7 +109,6 @@
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
</el-card>
@ -105,13 +117,21 @@
<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>
@ -133,10 +153,20 @@
<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>
@ -144,10 +174,18 @@
<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>
@ -157,41 +195,90 @@
</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>
@ -201,7 +288,10 @@
<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'
@ -218,14 +308,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)
@ -241,8 +331,8 @@ const memberParams = reactive({
loading: false,
data: [],
searchParam: {
id: id
}
id: id,
},
})
const getManjianMemberPageListFn = (page: number = 1) => {
memberParams.loading = true
@ -250,12 +340,14 @@ 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
})
}
@ -263,12 +355,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))
@ -276,7 +368,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

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

Loading…
Cancel
Save