后台商品详情多规格批量设置等

This commit is contained in:
pushuo 2023-01-31 14:59:04 +08:00 committed by Edward Yang
parent ed31084759
commit 8451c0b819
11 changed files with 149 additions and 13 deletions

View File

@ -151,7 +151,11 @@ function type_route($type, $value): string
} elseif ($type == 'static') {
return shop_route($value);
} elseif ($type == 'custom') {
return $value;
if (Str::startsWith($value, ['http://', 'https://'])) {
return $value;
} else {
return "//{$value}";
}
}
return '';

View File

@ -244,8 +244,8 @@ class ProductRepo
public static function getFilterPrice($data)
{
$selectPrice = $data['price'] ?? '-';
unset($data['price']);
$builder = self::getBuilder($data)->leftJoin('product_skus as ps', 'products.id', 'ps.product_id')
// unset($data['price']);
$builder = self::getBuilder(['category_id' => $data['category_id']])->leftJoin('product_skus as ps', 'products.id', 'ps.product_id')
->where('ps.is_default', 1);
$min = $builder->min('ps.price');
$max = $builder->max('ps.price');

View File

@ -80,6 +80,10 @@
}
}
.max-h-100 {
max-height: 100%;
}
.col-form-label.required {
&::before {
content: "*";

View File

@ -21,6 +21,12 @@ body.page-product-form {
}
}
.batch-setting {
.form-control {
max-width: 100px;
}
}
.variant-value-img {
width: 22px;
height: 22px;

View File

@ -62,7 +62,7 @@
:list="form.images"
:options="{animation: 200, handle: '.product-item'}"
>
<div v-for="image, index in form.images" :key="index" class="wh-80 product-item position-relative me-2 mb-2">
<div v-for="image, index in form.images" :key="index" class="wh-80 product-item position-relative me-2 mb-2 border d-flex justify-content-center align-items-center max-h-100 overflow-hidden">
<div class="position-absolute top-0 end-0">
<button class="btn btn-danger btn-sm wh-20 p-0" @click="removeImages(index)" type="button"><i class="bi bi-trash"></i></button>
</div>
@ -168,7 +168,29 @@
<el-button type="primary" plain size="small" @click="modalVariantOpenButtonClicked(-1, null)" class="btn btn-xs mr-1 mb-1">{{ __('admin/product.add_variable') }}</el-button>
</div>
<div v-if="form.skus.length && form.variables.length" class="mt-3 table-push">
<div v-if="form.skus.length && form.variables.length" class="mt-3 table-push table-responsive">
<div class="batch-setting d-flex align-items-center mb-3 p-2 bg-body">
<div v-for="(variant, index) in form.variables" :key="index" class="me-2">
<select class="form-select me-2 bg-white" aria-label="Default select example" v-model="variablesBatch.variables[index]">
<option selected value="">@{{ variant.name[current_language_code] || 'No name' }}</option>
<option :value="value_index" v-for="value, value_index in variant.values" :key="index+'-'+value_index" >
@{{ value.name[current_language_code]}}
</option>
</select>
</div>
<div role="button" class="border d-flex justify-content-center align-items-center border-dashed bg-light wh-40 me-2 bg-white" @click="batchSettingVariantImage">
<img :src="thumbnail(variablesBatch.image)" class="img-fluid" v-if="variablesBatch.image">
<i class="bi bi-plus fs-3 text-muted" v-else></i>
</div>
<input type="text" class="form-control me-2 bg-white" v-model="variablesBatch.model" placeholder="{{ __('admin/product.model') }}">
<input type="text" class="form-control me-2 bg-white" v-model="variablesBatch.sku" placeholder="sku">
<input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.price" placeholder="{{ __('admin/product.price') }}">
<input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.origin_price" placeholder="{{ __('admin/product.origin_price') }}">
<input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.cost_price" placeholder="{{ __('admin/product.cost_price') }}">
<input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.quantity" placeholder="{{ __('admin/product.quantity') }}">
<button type="button" class="btn btn-primary text-nowrap" @click="batchSettingVariant">{{ __('common.batch_setting') }}</button>
</div>
<table class="table table-bordered table-hover">
<thead>
<th v-for="(variant, index) in form.variables" :key="'pv-header-' + index">
@ -200,7 +222,7 @@
<input type="hidden" class="form-control" v-model="sku.images[index]" :name="'skus[' + skuIndex + '][images][]'"
placeholder="image">
</div>
<div class="border d-flex justify-content-center align-items-center border-dashed bg-light wh-40" @click="addProductImages(skuIndex)"><i class="bi bi-plus fs-3 text-muted"></i></div>
<div class="border d-flex justify-content-center align-items-center border-dashed bg-light wh-40" role="button" @click="addProductImages(skuIndex)"><i class="bi bi-plus fs-3 text-muted"></i></div>
</div>
<input type="hidden" class="form-control" :name="'skus[' + skuIndex + '][is_default]'" :value="skuIndex == 0 ? 1 : 0">
<input v-for="(variantValueIndex, j) in sku.variants" type="hidden"
@ -383,7 +405,7 @@
</div>
<x-admin::form.row title="">
<button type="submit" class="btn btn-primary mt-3 btn-lg">{{ __('common.save') }}</button>
<button type="button" @click="productsSubmit" class="btn btn-primary btn-submit mt-3 btn-lg">{{ __('common.save') }}</button>
</x-admin::form.row>
<el-dialog
@ -457,6 +479,18 @@
skus: @json($product->skus ?? []),
},
variablesBatch: {
variables: [],
model: '',
sku: '',
price: '',
origin_price: '',
cost_price: '',
quantity: '',
image: '',
status: false,
},
relations: {
keyword: '',
products: @json($relations ?? []),
@ -491,6 +525,7 @@
rules: {}
},
computed: {
// variant value 重复次数
variantValueRepetitions() {
@ -509,6 +544,13 @@
return (this.form.skus.length && this.form.skus[0].variants.length) || ''
}
},
beforeMount() {
if (this.form.variables.length) {
this.variablesBatch.variables = this.form.variables.map((v, i) => '');
}
},
watch: {
'source.variables': {
deep: true,
@ -525,13 +567,27 @@
}
this.form.variables = variants;
// 在 variablesBatch.variables 生成对应的 variants 的index
this.variablesBatch.variables = variants.map((v, i) => '');
if (this.isMove) return;
this.remakeSkus();
}
}
},
methods: {
// 表单提交,检测是否开启多规格 做处理
productsSubmit() {
if (!this.editing.isVariable) {
this.source.variables = [];
}
setTimeout(() => {
$('form#app').submit();
}, 0);
},
relationsQuerySearch(keyword, cb) {
$http.get('products/autocomplete?name=' + encodeURIComponent(keyword), null, {hload:true}).then((res) => {
cb(res.data);
@ -550,9 +606,9 @@
},
isVariableChange(e) {
if (!e) {
this.source.variables = [];
}
// if (!e) {
// this.source.variables = [];
// }
},
variantIsImage(e, index) {
@ -563,6 +619,12 @@
}
},
batchSettingVariantImage() {
bk.fileManagerIframe(images => {
this.variablesBatch.image = images[0].path;
})
},
addProductImages(skuIndex) {
bk.fileManagerIframe(images => {
if (!isNaN(skuIndex)) {
@ -585,6 +647,61 @@
this.form.skus[variantIndex].images.splice(index, 1)
},
batchSettingVariant() {
// 要修改的 skuIndex 下标
let setSkuIndex = [];
this.form.skus.forEach((sku, skuIndex) => {
this.variablesBatch.variables.forEach((variantIndex, index) => {
if (variantIndex !== '') {
// 根据 variantIndex, index修改 sku.variants[index] 的值
if (sku.variants[index] == variantIndex) {
setSkuIndex.push(skuIndex);
}
return;
}
// 如果 variantIndex 全部为空,就把所有的 skuIndex 都加入到 setSkuIndex 中
if (this.variablesBatch.variables.every(v => v === '')) {
setSkuIndex.push(skuIndex);
}
})
// 修改 skuIndex 下标对应的 sku
setSkuIndex.forEach((index) => {
if (this.variablesBatch.model) {
this.form.skus[index].model = this.variablesBatch.model + '-' + (index + 1);
}
if (this.variablesBatch.sku) {
this.form.skus[index].sku = this.variablesBatch.sku + '-' + (index + 1);
}
if (this.variablesBatch.image) {
this.form.skus[index].images = [this.variablesBatch.image];
}
if (this.variablesBatch.price) {
this.form.skus[index].price = this.variablesBatch.price;
}
if (this.variablesBatch.origin_price) {
this.form.skus[index].origin_price = this.variablesBatch.origin_price;
}
if (this.variablesBatch.cost_price) {
this.form.skus[index].cost_price = this.variablesBatch.cost_price;
}
if (this.variablesBatch.quantity) {
this.form.skus[index].quantity = this.variablesBatch.quantity;
}
})
})
// this.variablesBatch 对象内除了 variables 之外的值都清空
for (let key in this.variablesBatch) {
if (key !== 'variables') {
this.variablesBatch[key] = '';
}
}
},
dialogVariablesFormSubmit(form) {
const name = JSON.parse(JSON.stringify(this.dialogVariables.form.name));
const variantIndex = this.dialogVariables.variantIndex;

View File

@ -267,7 +267,6 @@ header {
#offcanvas-right-cart {
.select-wrap {
flex: 1;
margin-right: 10px;
cursor: pointer;

View File

@ -151,7 +151,8 @@ body.page-categories {
list-style: none;
li {
margin-top: 0;
line-height: 1;
margin: 16px 0;
ul {
margin-top: 8px;

View File

@ -76,6 +76,7 @@ return [
'text_list' => 'List',
'text_form' => 'From',
'text_to' => 'To',
'batch_setting' => 'Batch setting',
'id' => 'ID',
'created_at' => 'Created At',

View File

@ -76,6 +76,7 @@ return [
'text_list' => '列表',
'text_form' => '从',
'text_to' => '到',
'batch_setting' => '批量设置',
'id' => 'ID',
'created_at' => '创建时间',

View File

@ -75,6 +75,7 @@ return [
'text_list' => '列表',
'text_form' => '從',
'text_to' => '到',
'batch_setting' => '批量設置',
'id' => 'ID',
'created_at' => '創建時間',

View File

@ -55,7 +55,9 @@
<h1 class="mb-4 product-name">{{ $product['name'] }}</h1>
<div class="price-wrap d-flex align-items-end">
<div class="new-price fs-1 lh-1 fw-bold me-2">@{{ product.price_format }}</div>
<div class="old-price text-muted text-decoration-line-through">@{{ product.origin_price_format }}</div>
<div class="old-price text-muted text-decoration-line-through" v-if="product.price != product.origin_price">
@{{ product.origin_price_format }}
</div>
</div>
<div class="stock-and-sku mb-4">
<div class="d-flex">