!86 Optimization Advanced filters, taxes, currencies, etc.

* Fix region configuration province selection problem
* Optimization Advanced filters, taxes, currencies, etc.
This commit is contained in:
蒲硕 2023-04-26 08:38:24 +00:00 committed by Edward Yang
parent 2956a536ff
commit 76b37ed867
31 changed files with 409 additions and 212 deletions

View File

@ -0,0 +1,43 @@
<?php
/**
* AttributeController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-04 19:45:41
* @modified 2023-01-04 19:45:41
*/
namespace Beike\Admin\Http\Controllers;
use Beike\Admin\Repositories\AttributeRepo;
use Beike\Repositories\SettingRepo;
use Illuminate\Http\Request;
class MultiFilterController extends Controller
{
public function index()
{
$multiFilter = system_setting('base.multi_filter');
if ($attributeIds = $multiFilter['attribute'] ?? []) {
$multiFilter['attribute'] = AttributeRepo::getByIds($attributeIds);
}
$data = [
'multi_filter' => $multiFilter,
];
return view('admin::pages.multi_filter.index', $data);
}
public function store(Request $request)
{
$settings = $request->all();
foreach ($settings as $key => $value) {
SettingRepo::storeValue($key, $value);
}
return redirect(admin_route('multi_filter.index'))->with('success', trans('common.updated_success'));
}
}

View File

@ -12,7 +12,6 @@
namespace Beike\Admin\Http\Controllers;
use Beike\Admin\Http\Resources\CustomerGroupDetail;
use Beike\Admin\Repositories\AttributeRepo;
use Beike\Repositories\CountryRepo;
use Beike\Repositories\CurrencyRepo;
use Beike\Repositories\CustomerGroupRepo;
@ -35,15 +34,10 @@ class SettingController extends Controller
['value' => 'shipping', 'label' => trans('admin/setting.shipping_address')],
['value' => 'payment', 'label' => trans('admin/setting.payment_address')],
];
$multiFilter = system_setting('base.multi_filter');
if ($attributeIds = $multiFilter['attribute'] ?? []) {
$multiFilter['attribute'] = AttributeRepo::getByIds($attributeIds);
}
$data = [
'countries' => CountryRepo::listEnabled(),
'currencies' => CurrencyRepo::listEnabled(),
'multi_filter' => $multiFilter,
'tax_address' => $taxAddress,
'customer_groups' => CustomerGroupDetail::collection(CustomerGroupRepo::list())->jsonSerialize(),
'themes' => $themes,

View File

@ -138,7 +138,7 @@ class PermissionRepo
*/
private function getProductPermissions(): array
{
$routes = ['products_index', 'products_create', 'products_show', 'products_update', 'products_delete', 'products_trashed', 'products_restore'];
$routes = ['products_index', 'products_create', 'products_show', 'products_update', 'products_delete', 'products_trashed', 'products_restore', 'products_filter_index', 'products_filter_update'];
$items = $this->getPermissionList('product', $routes);
return hook_filter('role.product_permissions', $items);

View File

@ -32,6 +32,10 @@ Route::prefix($adminName)
Route::middleware('can:attributes_update')->put('attributes/{id}', [Controllers\AttributeController::class, 'update'])->name('attributes.update');
Route::middleware('can:attributes_delete')->delete('attributes/{id}', [Controllers\AttributeController::class, 'destroy'])->name('attributes.destroy');
// 高级筛选
Route::middleware('can:products_filter_index')->get('multi_filter', [Controllers\MultiFilterController::class, 'index'])->name('multi_filter.index');
Route::middleware('can:products_filter_update')->post('multi_filter', [Controllers\MultiFilterController::class, 'store'])->name('multi_filter.store');
// 属性组
Route::middleware('can:attribute_groups_index')->get('attribute_groups', [Controllers\AttributeGroupController::class, 'index'])->name('attribute_groups.index');
Route::middleware('can:attribute_groups_create')->post('attribute_groups', [Controllers\AttributeGroupController::class, 'store'])->name('attribute_groups.store');

View File

@ -122,7 +122,7 @@ class Sidebar extends Component
*/
private function getProductSubPrefix()
{
$prefix = ['products.', 'categories.', 'brands.', 'attribute_groups.', 'attributes.'];
$prefix = ['products.', 'multi_filter.', 'categories.', 'brands.', 'attribute_groups.', 'attributes.'];
return hook_filter('admin.sidebar.product.prefix', $prefix);
}
@ -190,11 +190,12 @@ class Sidebar extends Component
public function getProductSubRoutes()
{
$routes = [
['route' => 'categories.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'products.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'categories.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'brands.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
['route' => 'attribute_groups.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'attributes.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'multi_filter.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'products.trashed', 'icon' => 'fa fa-tachometer-alt'],
];

View File

@ -13,8 +13,6 @@
"axios": "^0.21",
"bootstrap": "^5.2.1",
"bootstrap-5.1.3": "npm:bootstrap@5.1.3",
"browser-sync": "^2.27.10",
"browser-sync-webpack-plugin": "^2.3.0",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
"resolve-url-loader": "^4.0.0",

View File

@ -524,4 +524,19 @@ table.table thead th, .fw-bold, h1,h2,h3, h4, h5, h6, b, strong, .card .card-hea
}
}
}
}
.active-line {
position: relative;
&:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: calc(100% + 1rem);
background-color: rgba($primary, 0.04);
border: 1px dashed rgba($primary, 0.5);
}
}

View File

@ -20,7 +20,7 @@
@hookwrapper('admin.header.vip')
<li class="nav-item vip-serve">
<a href="{{ config('beike.api_url') }}/vip/subscription?domain={{ config('app.url') }}&developer_token={{ system_setting('base.developer_token') }}" target="_blank" class="nav-link">
<a href="{{ config('beike.api_url') }}/vip/subscription?domain={{ config('app.url') }}&developer_token={{ system_setting('base.developer_token') }}&type=tab-vip" target="_blank" class="nav-link">
<img src="/image/vip-icon.png" class="img-fluid">
<span class="vip-text ms-1">VIP</span>
<div class="expired-text text-danger ms-2" style="display: none">@lang('admin/common.expired_at')<span class="ms-0"></span></div>
@ -28,6 +28,14 @@
</li>
@endhookwrapper
@hookwrapper('admin.header.license')
<li class="nav-item">
<a href="{{ config('beike.api_url') }}/vip/subscription?domain={{ config('app.url') }}&developer_token={{ system_setting('base.developer_token') }}&type=tab-license" target="_blank" class="nav-link">
<span class="vip-text ms-1">@lang('admin/common.copyright_buy')</span>
</a>
</li>
@endhookwrapper
@hookwrapper('admin.header.marketing')
<li class="nav-item">
<a href="{{ admin_route('marketing.index') }}" class="nav-link">@lang('admin/common.marketing')</a>

View File

@ -2,6 +2,10 @@
@section('title', __('admin/common.currency'))
@section('page-title-right')
<a href="{{ admin_route('settings.index') }}?tab=tab-checkout&line=rate_api_key" class="btn btn-outline-info" target="_blank">{{ __('admin/setting.rate_api_key') }}</a>
@endsection
@section('content')
<div id="tax-classes-app" class="card" v-cloak>
<div class="card-body h-min-600">

View File

@ -15,7 +15,7 @@
<th>ID</th>
<th>{{ __('common.name') }}</th>
<th>{{ __('admin/region.describe') }}</th>
<th>{{ __('customer_group.level') }}</th>
{{-- <th>{{ __('customer_group.level') }}</th> --}}
<th>{{ __('common.created_at') }}</th>
<th width="130px">{{ __('common.action') }}</th>
</tr>
@ -28,7 +28,7 @@
<div :title="group.description?.description || ''" class="w-max-500">
@{{ stringLengthInte(group.description?.description || '') }}</div>
</td>
<td>@{{ group.level }}</td>
{{-- <td>@{{ group.level }}</td> --}}
<td>@{{ group.created_at }}</td>
<td>
<button class="btn btn-outline-secondary btn-sm" @click="checkedCustomersCreate('edit', index)">{{ __('common.edit') }}</button>

View File

@ -0,0 +1,114 @@
@extends('admin::layouts.master')
@section('title', __('admin/common.multi_filter_index'))
@section('content')
<div class="card">
<div class="card-body h-min-600">
<form action="{{ admin_route('multi_filter.store') }}" class="needs-validation" novalidate method="POST" id="app">
@csrf
@if (session('success'))
<x-admin-alert type="success" msg="{{ session('success') }}" class="mt-4"/>
@endif
<h6 class="border-bottom pb-3 mb-4">{{ __('common.data') }}</h6>
<x-admin::form.row :title="__('admin/setting.filter_attribute')">
<div class="module-edit-group wp-600">
<div class="autocomplete-group-wrapper">
<el-autocomplete class="inline-input" v-model="multi_filter.keyword" value-key="name" size="small"
:fetch-suggestions="(keyword, cb) => {attributesQuerySearch(keyword, cb, 'products')}"
placeholder="{{ __('admin/builder.modules_keywords_search') }}"
@select="(e) => {handleSelect(e, 'product_attributes')}"></el-autocomplete>
<div class="item-group-wrapper" v-loading="multi_filter.loading">
<template v-if="multi_filter.filters.attribute.length">
<div v-for="(item, index) in multi_filter.filters.attribute" :key="index" class="item">
<div>
<i class="el-icon-s-unfold"></i>
<span>@{{ item.name }}</span>
</div>
<i class="el-icon-delete right" @click="attributesRemoveProduct(index)"></i>
<input type="text" :name="'multi_filter[attribute]['+ index +']'" v-model="item.id"
class="form-control d-none">
</div>
</template>
<template v-else>
{{ __('admin/setting.please_select') }}
<input type="text" name="multi_filter" value="" class="form-control d-none">
</template>
</div>
<div class="help-text font-size-12 lh-base">{{ __('admin/setting.multi_filter_helper') }}</div>
</div>
</div>
</x-admin::form.row>
<x-admin-form-switch name="multi_filter[price_filter]" :title="__('admin/multi_filter.price_filter')" :value="old('price_filter', $multi_filter['price_filter'] ?? 1)" />
<x-admin::form.row title="">
<button class="btn btn-lg btn-primary mt-5">{{ __('common.save') }}</button>
</x-admin::form.row>
</form>
</div>
</div>
@endsection
@push('footer')
<script>
let app = new Vue({
el: '#app',
data: {
multi_filter: {
keyword: '',
filters: @json($multi_filter ?? null),
loading: null,
},
source: {
mailEngines: [
{name: '{{ __('admin/builder.text_no') }}', code: ''},
{name: 'SMTP', code: 'smtp'},
{name: 'Sendmail', code: 'sendmail'},
{name: 'Mailgun', code: 'mailgun'},
{name: 'Log', code: 'log'},
]
},
},
created() {
const multi_filter = @json($multi_filter ?? null);
if (multi_filter) {
this.multi_filter.filters = multi_filter;
} else {
this.multi_filter.filters = {
attribute: [],
}
}
},
methods: {
attributesQuerySearch(keyword, cb, url) {
$http.get('attributes/autocomplete?name=' + encodeURIComponent(keyword), null, {hload:true}).then((res) => {
cb(res.data);
})
},
attributesRemoveProduct(index) {
this.multi_filter.filters.attribute.splice(index, 1);
},
handleSelect(item, key) {
if (key == 'product_attributes') {
if (!this.multi_filter.filters.attribute.find(v => v.id * 1 == item.id * 1)) {
this.multi_filter.filters.attribute.push(item);
} else {
layer.msg('{{ __('common.no_repeat') }}', () => {})
}
this.multi_filter.keyword = ""
}
},
}
});
</script>
@endpush

View File

@ -3,98 +3,108 @@
@section('title', __('admin/region.index'))
@section('content')
<div id="tax-classes-app" class="card" v-cloak>
<div class="card-body h-min-600">
<div class="d-flex justify-content-between mb-4">
<button type="button" class="btn btn-primary" @click="checkedCreate('add', null)">{{ __('common.add') }}</button>
</div>
<div class="table-push">
<table class="table">
<div id="tax-classes-app" class="card" v-cloak>
<div class="card-body h-min-600">
<div class="d-flex justify-content-between mb-4">
<button type="button" class="btn btn-primary" @click="checkedCreate('add', null)">{{ __('common.add') }}</button>
</div>
<div class="table-push">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>{{ __('admin/region.name') }}</th>
<th>{{ __('admin/region.describe') }}</th>
<th>{{ __('common.created_at') }}</th>
<th>{{ __('common.updated_at') }}</th>
<th class="text-end">{{ __('common.action') }}</th>
</tr>
</thead>
<tbody v-if="regions.length">
<tr v-for="tax, index in regions" :key="index">
<td>@{{ tax.id }}</td>
<td>@{{ tax.name }}</td>
<td :title="tax.description">@{{ stringLengthInte(tax.description) }}</td>
<td>@{{ tax.created_at }}</td>
<td>@{{ tax.updated_at }}</td>
<td class="text-end">
<button class="btn btn-outline-secondary btn-sm" @click="checkedCreate('edit', index)">{{
__('common.edit') }}</button>
<button class="btn btn-outline-danger btn-sm ml-1" type="button" @click="deleteCustomer(tax.id, index)">{{
__('common.delete') }}</button>
</td>
</tr>
</tbody>
<tbody v-else>
<tr>
<td colspan="6" class="border-0">
<x-admin-no-data />
</td>
</tr>
</tbody>
</table>
</div>
</div>
<el-dialog title="{{ __('admin/region.regions_create') }}" :visible.sync="dialog.show" width="700px"
@close="closeCustomersDialog('form')" :close-on-click-modal="false" @open="openDialog">
<el-form ref="form" :rules="rules" :model="dialog.form" label-width="120px">
<el-form-item label="{{ __('common.name') }}" prop="name">
<el-input v-model="dialog.form.name" placeholder="{{ __('common.name') }}"></el-input>
</el-form-item>
<el-form-item label="{{ __('admin/region.describe') }}" prop="description">
<el-input v-model="dialog.form.description" placeholder="{{ __('admin/region.describe') }}"></el-input>
</el-form-item>
<el-form-item label="{{ __('admin/region.index') }}">
<table class="table table-bordered" style="line-height: 1.6;">
<thead>
<tr>
<th>ID</th>
<th>{{ __('admin/region.name') }}</th>
<th>{{ __('admin/region.describe') }}</th>
<th>{{ __('common.created_at') }}</th>
<th>{{ __('common.updated_at') }}</th>
<th class="text-end">{{ __('common.action') }}</th>
<th>{{ __('admin/region.country') }}</th>
<th>{{ __('admin/region.zone') }}</th>
<th></th>
</tr>
</thead>
<tbody v-if="regions.length">
<tr v-for="tax, index in regions" :key="index">
<td>@{{ tax.id }}</td>
<td>@{{ tax.name }}</td>
<td :title="tax.description">@{{ stringLengthInte(tax.description) }}</td>
<td>@{{ tax.created_at }}</td>
<td>@{{ tax.updated_at }}</td>
<td class="text-end">
<button class="btn btn-outline-secondary btn-sm" @click="checkedCreate('edit', index)">{{ __('common.edit') }}</button>
<button class="btn btn-outline-danger btn-sm ml-1" type="button" @click="deleteCustomer(tax.id, index)">{{ __('common.delete') }}</button>
<tbody>
<tr v-for="rule, index in dialog.form.region_zones" :key="index">
<td>
<el-select v-model="rule.country_id" size="mini" filterable
placeholder="{{ __('admin/customer.choose_country') }}" @change="(e) => {countryChange(e, index)}">
<el-option v-for="item, option_index in source.countries" :key="index + '-' + option_index" :label="item.name" :value="item.id">
</el-option>
</el-select>
</td>
<td>
<el-select v-model="rule.zone_id" size="mini" filterable
placeholder="{{ __('admin/customer.choose_zones') }}">
<el-option v-for="item, option_index in rule.zones" :key="index + '-' + option_index" :label="item.name" :value="item.id">
</el-option>
</el-select>
</td>
<td>
<button class="btn btn-outline-danger btn-sm ml-1" type="button" @click="deleteRates(index)">{{
__('common.delete') }}</button>
</td>
</tr>
</tbody>
<tbody v-else><tr><td colspan="6" class="border-0"><x-admin-no-data /></td></tr></tbody>
</table>
</div>
</div>
<el-dialog title="{{ __('admin/region.regions_create') }}" :visible.sync="dialog.show" width="700px"
@close="closeCustomersDialog('form')" :close-on-click-modal="false">
<el-form ref="form" :rules="rules" :model="dialog.form" label-width="120px">
<el-form-item label="{{ __('common.name') }}" prop="name">
<el-input v-model="dialog.form.name" placeholder="{{ __('common.name') }}"></el-input>
</el-form-item>
<el-form-item label="{{ __('admin/region.describe') }}" prop="description">
<el-input v-model="dialog.form.description" placeholder="{{ __('admin/region.describe') }}"></el-input>
</el-form-item>
<el-form-item label="{{ __('admin/region.index') }}">
<table class="table table-bordered" style="line-height: 1.6;">
<thead>
<tr>
<th>{{ __('admin/region.country') }}</th>
<th>{{ __('admin/region.zone') }}</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="rule, index in dialog.form.region_zones" :key="index">
<td>
<el-select v-model="rule.country_id" size="mini" filterable placeholder="{{ __('admin/customer.choose_country') }}" @change="(e) => {countryChange(e, index)}">
<el-option v-for="item in source.countries" :key="item.id" :label="item.name"
:value="item.id">
</el-option>
</el-select>
</td>
<td>
<el-select v-model="rule.zone_id" size="mini" filterable placeholder="{{ __('admin/customer.choose_zones') }}">
<el-option v-for="item in rule.zones" :key="item.id" :label="item.name"
:value="item.id">
</el-option>
</el-select>
</td>
<td>
<button class="btn btn-outline-danger btn-sm ml-1" type="button" @click="deleteRates(index)">{{ __('common.delete') }}</button>
</td>
</tr>
</tbody>
</table>
<el-button type="primary" icon="el-icon-plus" size="small" plain @click="addRates">{{ __('common.add') }}</el-button>
</el-form-item>
<el-form-item class="mt-5">
<el-button type="primary" @click="addFormSubmit('form')">{{ __('common.save') }}</el-button>
<el-button @click="closeCustomersDialog('form')">{{ __('common.cancel') }}</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
<el-button type="primary" icon="el-icon-plus" size="small" plain @click="addRates">{{ __('common.add') }}
</el-button>
</el-form-item>
<el-form-item class="mt-5">
<el-button type="primary" @click="addFormSubmit('form')">{{ __('common.save') }}</el-button>
<el-button @click="closeCustomersDialog('form')">{{ __('common.cancel') }}</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
@endsection
@push('footer')
<script>
new Vue({
<script>
new Vue({
el: '#tax-classes-app',
data: {
@ -129,7 +139,10 @@
// 在挂载开始之前被调用:相关的 render 函数首次被调用
beforeMount() {
$http.get(`countries/${this.source.country_id}/zones`).then((res) => {
this.dialog.zones = res.data.zones
this.dialog.zones = [
{name: '{{ __('common.please_choose') }}', id: 0},
...res.data.zones
]
})
},
@ -138,13 +151,16 @@
this.dialog.show = true
this.dialog.type = type
this.dialog.index = index
},
if (type == 'edit') {
let tax = this.regions[index];
openDialog() {
if (this.dialog.type == 'edit') {
let tax = this.regions[this.dialog.index];
tax.region_zones.forEach(e => {
$http.get(`countries/${e.country_id}/zones`).then((res) => {
this.$set(e, 'zones', res.data.zones)
let zones = [{name: '{{ __('common.please_choose') }}', id: 0}, ...res.data.zones]
this.$set(e, 'zones', zones)
})
})
@ -163,7 +179,7 @@
this.dialog.form.region_zones.push({
country_id: this.source.country_id,
zone_id: '',
zone_id: 0,
zones: this.dialog.zones,
})
},
@ -196,8 +212,11 @@
countryChange(e, index) {
$http.get(`countries/${e}/zones`).then((res) => {
this.dialog.form.region_zones[index].zones = res.data.zones
this.dialog.form.region_zones[index].zone_id = ''
this.dialog.form.region_zones[index].zones = [
{name: '{{ __('common.please_choose') }}', id: 0},
...res.data.zones
]
this.dialog.form.region_zones[index].zone_id = 0
})
},
@ -222,5 +241,5 @@
}
}
})
</script>
@endpush
</script>
@endpush

View File

@ -5,6 +5,7 @@
@section('content')
<div id="customer-app" class="card h-min-600">
<div class="card-body">
<div class="mb-2">{{ __('admin/rma.rma_list_title') }}</div>
@if (count($rmas))
<div class="table-push">
<table class="table">

View File

@ -30,9 +30,6 @@
<li class="nav-item" role="presentation">
<a class="nav-link" data-bs-toggle="tab" href="#tab-express-company">{{ __('order.express_company') }}</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" data-bs-toggle="tab" href="#tab-multi-filter">{{ __('admin/setting.multi_filter') }}</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" data-bs-toggle="tab" href="#tab-mail">{{ __('admin/setting.mail_settings') }}</a>
</li>
@ -119,42 +116,6 @@
@hook('admin.setting.image.after')
</div>
<div class="tab-pane fade" id="tab-multi-filter">
<x-admin::form.row :title="__('admin/setting.filter_attribute')">
<div class="module-edit-group wp-600">
<div class="autocomplete-group-wrapper">
<el-autocomplete
class="inline-input"
v-model="multi_filter.keyword"
value-key="name"
size="small"
:fetch-suggestions="(keyword, cb) => {attributesQuerySearch(keyword, cb, 'products')}"
placeholder="{{ __('admin/builder.modules_keywords_search') }}"
@select="(e) => {handleSelect(e, 'product_attributes')}"
></el-autocomplete>
<div class="item-group-wrapper" v-loading="multi_filter.loading">
<template v-if="multi_filter.filters.attribute.length">
<div v-for="(item, index) in multi_filter.filters.attribute" :key="index" class="item">
<div>
<i class="el-icon-s-unfold"></i>
<span>@{{ item.name }}</span>
</div>
<i class="el-icon-delete right" @click="attributesRemoveProduct(index)"></i>
<input type="text" :name="'multi_filter[attribute]['+ index +']'" v-model="item.id" class="form-control d-none">
</div>
</template>
<template v-else>
{{ __('admin/setting.please_select') }}
<input type="text" name="multi_filter" value="" class="form-control d-none">
</template>
</div>
<div class="help-text font-size-12 lh-base">{{ __('admin/setting.multi_filter_helper') }}</div>
</div>
</div>
</x-admin::form.row>
</div>
<div class="tab-pane fade" id="tab-express-company">
@hook('admin.setting.express.before')
<x-admin::form.row title="{{ __('order.express_company') }}">
@ -293,24 +254,31 @@
}
$(function() {
const [tab, line] = [bk.getQueryString('tab'), bk.getQueryString('line')];
getZones(country_id);
$('select[name="country_id"]').on('change', function () {
getZones($(this).val());
});
if (tab) {
$(`a[href="#${tab}"]`)[0].click()
}
if (line) {
$(`textarea[name="${line}"], select[name="${line}"], input[name="${line}"]`).parents('.row').addClass('active-line');
setTimeout(() => {
$('div').removeClass('active-line');
}, 1200);
}
});
</script>
<script>
new Vue({
let app = new Vue({
el: '#app',
data: {
multi_filter: {
keyword: '',
filters: @json($multi_filter ?? null),
loading: null,
},
mail_engine: @json(old('mail_engine', system_setting('base.mail_engine', ''))),
express_company: @json(old('express_company', system_setting('base.express_company', []))),
@ -324,16 +292,6 @@
]
},
},
created() {
const multi_filter = @json($multi_filter ?? null);
if (multi_filter) {
this.multi_filter.filters = multi_filter;
} else {
this.multi_filter.filters = {
attribute: [],
}
}
},
methods: {
addCompany() {
if (typeof this.express_company == 'string') {
@ -342,35 +300,8 @@
this.express_company.push({name: '', code: ''})
},
attributesQuerySearch(keyword, cb, url) {
$http.get('attributes/autocomplete?name=' + encodeURIComponent(keyword), null, {hload:true}).then((res) => {
cb(res.data);
})
},
attributesRemoveProduct(index) {
this.multi_filter.filters.attribute.splice(index, 1);
},
handleSelect(item, key) {
if (key == 'product_attributes') {
if (!this.multi_filter.filters.attribute.find(v => v.id * 1 == item.id * 1)) {
this.multi_filter.filters.attribute.push(item);
} else {
layer.msg('{{ __('common.no_repeat') }}', () => {})
}
this.multi_filter.keyword = ""
}
},
}
});
const tab = bk.getQueryString('tab');
if (tab) {
$(`a[href="#${bk.getQueryString('tab')}"]`)[0].click()
}
</script>
@endpush

View File

@ -2,6 +2,10 @@
@section('title', __('admin/tax_rate.tax_classes_index'))
@section('page-title-right')
<a href="{{ admin_route('settings.index') }}?tab=tab-checkout&line=tax_address" class="btn btn-outline-info" target="_blank">{{ __('admin/setting.tax_address') }}</a>
@endsection
@section('content')
<ul class="nav-bordered nav nav-tabs mb-3">
<li class="nav-item">

View File

@ -2,6 +2,10 @@
@section('title', __('admin/tax_rate.index'))
@section('page-title-right')
<a href="{{ admin_route('settings.index') }}?tab=tab-checkout&line=tax_address" class="btn btn-outline-info" target="_blank">{{ __('admin/setting.tax_address') }}</a>
@endsection
@section('content')
<ul class="nav-bordered nav nav-tabs mb-3">
<li class="nav-item">

View File

@ -49,8 +49,10 @@ return [
'country' => 'Country',
'file_manager' => 'File Manager',
'access_frontend' => 'Frontend',
'copyright_buy' => 'Copyright Buy',
// sidebar
'multi_filter_index' => 'Advanced Filter',
'theme_index' => 'Theme Setting',
'attribute_groups_index' => 'Attribute Group',
'attributes_index' => 'Attributes',

View File

@ -0,0 +1,14 @@
<?php
/**
* header.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-08-02 19:03:19
* @modified 2022-08-02 19:03:19
*/
return [
'price_filter' => 'Price Filter',
];

View File

@ -18,6 +18,8 @@ return [
'products_trashed' => 'Trashed',
'products_restore' => 'Restore',
'clear_restore' => 'Empty Recycle Bin',
'products_filter_index' => 'View Filters',
'products_filter_update' => 'Modify Filters',
'batch_delete' => 'Batch Delete',
'batch_active' => 'Batch Active',

View File

@ -17,6 +17,7 @@ return [
'rmas_update' => 'Update',
'rmas_delete' => 'Delete',
'rma_list_title' => 'Rmas application list',
'customers_name' => 'Customers Name',
'quantity' => 'Quantity',
'service_type' => 'Service Type',

View File

@ -73,6 +73,6 @@ return [
'rate_api_key' => 'Exchange rate API KEY',
'multi_filter' => 'Multi Filter',
'please_select' => 'Please select',
'multi_filter_helper' => 'Please select the attributes to be displayed in the Multi Filter module.',
'multi_filter_helper' => 'Please select the attributes that need to be displayed in the filter area of the product list at the front desk, if left blank, all will be displayed',
'filter_attribute' => 'Attribute Filter',
];

View File

@ -49,8 +49,10 @@ return [
'country' => '国家管理',
'file_manager' => '文件管理器',
'access_frontend' => '访问前台',
'copyright_buy' => '版权购买',
// sidebar
'multi_filter_index' => '高级筛选',
'theme_index' => '模板设置',
'attribute_groups_index' => '属性组',
'attributes_index' => '属性',

View File

@ -0,0 +1,14 @@
<?php
/**
* header.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-08-02 19:03:19
* @modified 2022-08-02 19:03:19
*/
return [
'price_filter' => '价格筛选',
];

View File

@ -18,6 +18,8 @@ return [
'products_trashed' => '回收站',
'products_restore' => '恢复回收站',
'clear_restore' => '清空回收站',
'products_filter_index' => '查看高级筛选',
'products_filter_update' => '修改高级筛选',
'batch_delete' => '批量删除',
'batch_active' => '批量上架',

View File

@ -10,17 +10,18 @@
*/
return [
'index' => '售后申请',
'index' => '售后管理',
'rmas_index' => '售后服务列表',
'rmas_show' => '售后服务详情',
'rmas_update' => '更新售后服务',
'rmas_delete' => '删除售后服务',
'rma_list_title' => '售后申请列表',
'customers_name' => '客户姓名',
'quantity' => '数量',
'service_type' => '服务类型',
'rma_details' => '售后申请详情',
'rma_details' => '售后管理详情',
'reasons_return' => '退货原因',
'current_state' => '当前状态',
'modify_status' => '修改状态',

View File

@ -71,6 +71,6 @@ return [
'rate_api_key' => '汇率 API KEY',
'multi_filter' => '高级筛选',
'please_select' => '请添加',
'multi_filter_helper' => '请选择需要在筛选模块显示的属性',
'multi_filter_helper' => '请选择需要在前台商品列表筛选区域显示的属性,留空则显示全部',
'filter_attribute' => '属性筛选',
];

View File

@ -49,8 +49,10 @@ return [
'country' => '國家管理',
'file_manager' => '文件管理器',
'access_frontend' => '訪問前台',
'copyright_buy' => '版權購買',
// sidebar
'multi_filter_index' => '高級篩選',
'theme_index' => '模板設置',
'attribute_groups_index' => '屬性組',
'attributes_index' => '屬性',

View File

@ -0,0 +1,14 @@
<?php
/**
* header.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-08-02 19:03:19
* @modified 2022-08-02 19:03:19
*/
return [
'price_filter' => '價格篩選',
];

View File

@ -17,6 +17,7 @@ return [
'rmas_update' => '更新售後服務',
'rmas_delete' => '刪除售後服務',
'rma_list_title' => '售後申請列表',
'customers_name' => '客戶姓名',
'quantity' => '數量',
'service_type' => '服務類型',

View File

@ -72,6 +72,6 @@ return [
'rate_api_key' => '匯率 API KEY',
'multi_filter' => '高級篩選',
'please_select' => '請添加',
'multi_filter_helper' => '請選擇需要在篩選模塊顯示的屬性',
'multi_filter_helper' => '請選擇需要在前台商品列表篩選區域顯示的屬性,留空則顯示全部',
'filter_attribute' => '屬性篩選',
];

View File

@ -27,28 +27,30 @@
<link rel="stylesheet" href="{{ asset('vendor/jquery/jquery-ui/jquery-ui.min.css') }}">
@endpush
<div class="card">
<div class="card-header p-0">
<h4 class="mb-3">{{ __('product.price') }}</h4>
</div>
<div class="card-body p-0">
<div id="price-slider" class="mb-2"><div class="slider-bg"></div></div>
<div class="text-secondary price-range d-flex justify-content-between">
<div>
{{ __('common.text_form') }}
<span class="min">{{ currency_format($filter_data['price']['select_min'], current_currency_code()) }}</span>
</div>
<div>
{{ __('common.text_to') }}
<span class="max">{{ currency_format($filter_data['price']['select_max'], current_currency_code()) }}</span>
</div>
@if (system_setting('base.multi_filter.price_filter', 1))
<div class="card">
<div class="card-header p-0">
<h4 class="mb-3">{{ __('product.price') }}</h4>
</div>
<div class="card-body p-0">
<div id="price-slider" class="mb-2"><div class="slider-bg"></div></div>
<div class="text-secondary price-range d-flex justify-content-between">
<div>
{{ __('common.text_form') }}
<span class="min">{{ currency_format($filter_data['price']['select_min'], current_currency_code()) }}</span>
</div>
<div>
{{ __('common.text_to') }}
<span class="max">{{ currency_format($filter_data['price']['select_max'], current_currency_code()) }}</span>
</div>
</div>
<input value="{{ $filter_data['price']['select_min'] }}" class="price-select-min d-none">
<input value="{{ $filter_data['price']['select_max'] }}" class="price-select-max d-none">
<input value="{{ $filter_data['price']['min'] }}" class="price-min d-none">
<input value="{{ $filter_data['price']['max'] }}" class="price-max d-none">
</div>
<input value="{{ $filter_data['price']['select_min'] }}" class="price-select-min d-none">
<input value="{{ $filter_data['price']['select_max'] }}" class="price-select-max d-none">
<input value="{{ $filter_data['price']['min'] }}" class="price-min d-none">
<input value="{{ $filter_data['price']['max'] }}" class="price-max d-none">
</div>
</div>
@endif
@endhookwrapper
@endif