修改:物流支持多国家选择

This commit is contained in:
wuhui_zzw 2023-08-21 16:59:20 +08:00
parent 3a7aacb474
commit afa9b0be6f
8 changed files with 418 additions and 45 deletions

View File

@ -9,8 +9,10 @@ use Beike\Admin\Repositories\TaxClassRepo;
use Beike\Admin\Services\LogisticsService;
use Beike\Admin\View\DesignBuilders\Product;
use Beike\Libraries\Weight;
use Beike\Models\Country;
use Beike\Models\Logistics;
use Beike\Repositories\CategoryRepo;
use Beike\Repositories\CountryRepo;
use Beike\Repositories\LanguageRepo;
use Beike\Repositories\LogisticsRepo;
use Illuminate\Http\Request;
@ -161,6 +163,10 @@ class LogisticsController extends Controller
['title' => 'num','name'=>trans('admin/logistics.type_num')],
['title' => 'free','name'=>trans('admin/logistics.type_free')],
];
// 获取
$countryIds = (array)explode(',',$logistics->country_ids);
$logistics->country_ids = CountryRepo::getInList($countryIds);
$data = [
'logistics' => $logistics,
'types' => $types,

View File

@ -49,6 +49,7 @@ class LogisticsService
try {
DB::beginTransaction();
$data['country_ids'] = (String) ($data['country_ids'] ? implode(',',$data['country_ids']) : '');
$data['country_id'] = (int) ($data['country_id'] ?? 0);
$data['throwing_ratio'] = (int) ($data['throwing_ratio'] ?? 0);
$data['day_min'] = (int) ($data['day_min'] ?? 0);
@ -60,12 +61,12 @@ class LogisticsService
$data['continuation_weight_fee'] = (float) ($data['continuation_weight_fee'] ?? 0);
$data['num_fee'] = (float) ($data['num_fee'] ?? 0);
$data['variables'] = json_decode($data['variables'] ?? '[]');
$logistics->fill($data);
$logistics->updated_at = now();
$logistics->save();
if ($isUpdating) {
}
if ($isUpdating) {}
DB::commit();

View File

@ -10,7 +10,23 @@ class Logistics extends Base
use HasFactory;
use SoftDeletes;
protected $fillable = ['name', 'warehouse_name', 'country_id', 'type', 'first_weight', 'first_weight_fee', 'continuation_weight_max', 'add_weight', 'continuation_weight_fee', 'throwing_ratio', 'num_fee', 'day_min', 'day_max','position'];
protected $fillable = [
'name',
'warehouse_name',
'country_id',
'country_ids',
'type',
'first_weight',
'first_weight_fee',
'continuation_weight_max',
'add_weight',
'continuation_weight_fee',
'throwing_ratio',
'num_fee',
'day_min',
'day_max',
'position'
];
public function country()
{

View File

@ -122,15 +122,50 @@ class CountryRepo
return Country::query()->select('id', 'name')->get();
}
public static function autocomplete($name, $onlyActive = 1)
{
/**
* Common: 查询国家列表
* Author: wu-hui
* Time: 2023/08/21 16:10
* @param $name
* @param int $onlyActive
* @return Builder[]|Collection
*/
public static function autocomplete($name, $onlyActive = 1){
// 参数获取
$pageSize = request()->input('page_size',10);
// 列表获取
$builder = Country::query()
->where('name', 'like', "$name%")
->select('id', 'name', 'status');
->select('id', 'name', 'status', 'code')
->orderBy('sort_order','DESC')
->orderBy('id','ASC');
// if ($onlyActive) {
// $builder->where('status', 1);
// }
return $builder->limit(10)->get();
if($pageSize == 'all') return $builder->get();
else return $builder->limit($pageSize)->get();
}
/**
* Common: 根据国家ID 获取对应的列表
* Author: wu-hui
* Time: 2023/08/21 16:13
* @param array $ids
* @return array|mixed[]
*/
public static function getInList(array $ids){
$list = Country::query()
->whereIn('id', $ids)
->select('id', 'name', 'code')
->orderBy('sort_order','DESC')
->orderBy('id','ASC')
->get();
if($list) {
$list = $list->toArray();
return array_column($list,null,'id');
}
return [];
}
}

View File

@ -25,17 +25,79 @@
@endforeach
</div>
@endif
<style>
.country-list{
margin-top: 10px;
width: 100%;
}
.country-list .list-box{
border: 1px solid #e7e7e7;
margin: 0 10px 10px 0;
border-radius: 5px;
padding-left: 10px;
height: 65px;
user-select: none;
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
}
.country-list .list-box .list-box-left{
display: inline-flex;
flex-direction: column;
flex-wrap: nowrap;
align-items: flex-start;
justify-content: center;
}
.country-list .list-box .list-box-left .item-title{
font-size: 18px;
font-weight: bold;
color: #707070;
width: 100%;
height: 30px;
line-height: 30px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.country-list .list-box .list-box-left .item-code{
font-size: 14px;
color: #656565;
height: 30px;
line-height: 30px;
}
.country-list .list-box .delete-btn{
height: 65px;
width: 65px;
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
cursor: pointer;
}
.country-list .list-box .delete-btn i{
background: #dc3545;
color: #fff;
width: 35px;
height: 35px;
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
border-radius: 50%;
}
</style>
<ul class="nav nav-tabs nav-bordered mb-3" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tab-basic" type="button" >{{ __('admin/logistics.basic_information') }}</button>
</li>
</ul>
<div class="card">
<div class="card" id="app">
<div class="card-body h-min-600">
<form novalidate class="needs-validation" action="{{ $logistics->id ? admin_route('logistics.update', $logistics) : admin_route('logistics.store') }}"
method="POST" id="app">
<form id="appForm" novalidate class="needs-validation" action="{{ $logistics->id ? admin_route('logistics.update', $logistics) : admin_route('logistics.store') }}" method="POST" >
@csrf
@method($logistics->id ? 'PUT' : 'POST')
<input type="hidden" name="_redirect" value="{{ $_redirect }}" />
@ -49,8 +111,25 @@
@hookwrapper('admin.logistics.edit.country')
<x-admin::form.row :title="__('admin/country.index')">
<input type="text" value="{{ $logistics->country->name ?? '' }}" id="country-autocomplete" class="form-control wp-400 " />
<input type="hidden" name="country_id" value="{{ old('country_id', $logistics->country_id ?? '') }}" />
{{--已选中地区列表--}}
<div class="country-list">
<div class="list-box" v-for="(item,index) in country_list" :title="item.name">
<div class="list-box-left">
<div class="item-title">@{{ item.name }}</div>
<div class="item-code">@{{ item.code }}</div>
<input type="hidden" name="country_ids[]" :value="item.id" />
</div>
<div class="delete-btn" @click="$refs['select-countries'].clickCountries(item)">
<i class="bi bi-trash-fill"></i>
</div>
</div>
</div>
{{--添加按钮--}}
<button type="button" class="btn btn-primary" @click="$refs['select-countries'].showSelectCountries()">
<i class="bi bi-plus"></i>
</button>
{{--<input type="text" value="{{ $logistics->country->name ?? '' }}" id="country-autocomplete" class="form-control wp-400 " />--}}
{{--<input type="hidden" name="country_id" value="{{ old('country_id', $logistics->country_id ?? '') }}" />--}}
</x-admin::form.row>
@endhookwrapper
@ -110,17 +189,21 @@
</x-admin::form.row>
</form>
</div>
<select-countries ref="select-countries" :country_list="country_list" @change-info="changeCountry"></select-countries>
</div>
@hook('admin.logistics.form.footer')
@hook('admin.logistics.form.footer')
@endsection
@push('footer')
@include('shared.select-countries')
<script>
$('.submit-form').on('click', function () {
setTimeout(() => {
$(`form#app`).find('button[type="submit"]')[0].click();
$(`form#appForm`).find('button[type="submit"]')[0].click();
}, 0);
})
@ -144,7 +227,6 @@
day_min: @json($logistics->day_min ?? ''),
day_max: @json($logistics->day_max ?? ''),
},
variablesBatch: {
variables: [],
model: '',
@ -156,22 +238,18 @@
image: '',
status: false,
},
relations: {
keyword: '',
logistics: @json($relations ?? []),
loading: null,
},
source: {
variables: @json($logistics->variables ?? []),
languages: @json($languages ?? []),
},
editing: {
isVariable: @json(($logistics->variables ?? NULL) != NULL),
},
dialogVariables: {
show: false,
variantIndex: null,
@ -180,7 +258,6 @@
name: {}
},
},
attributeDialog: {
show: false,
index: null,
@ -188,10 +265,10 @@
name: {},
}
},
rules: {}
rules: {},
// 已选择国家列表
country_list: @json($logistics->country_ids ?? []),
},
computed: {
// variant value 重复次数
variantValueRepetitions() {
@ -214,11 +291,7 @@
return (this.form.numPrices.length && this.form.price_setting === 'num') || this.form.price_setting === 'sku' || ''
}
},
beforeMount() {
},
beforeMount() {},
watch: {
'source.variables': {
deep: true,
@ -243,16 +316,13 @@
}
}
},
created() {
},
created() {},
methods: {
dev(){
dev() {
console.log(1);
return true;
},
logisticsType(e){
logisticsType(e) {
console.log(1);
},
addNumPrices() {
@ -271,7 +341,7 @@
},
relationsQuerySearch(keyword, cb) {
$http.get('logistics/autocomplete?name=' + encodeURIComponent(keyword), null, {hload:true}).then((res) => {
$http.get('logistics/autocomplete?name=' + encodeURIComponent(keyword), null, {hload: true}).then((res) => {
cb(res.data);
})
},
@ -479,7 +549,17 @@
this.source.variables.splice(variantIndex, 1);
if (!this.source.variables.length) {
setTimeout(() => { // 等 remakeSkus 完成 this.form.skus = [];
this.form.skus = [{logistics_sku_id: 0,position: 1,variants: [],image: '',model: '',sku: '',price: null,quantity: null,is_default: 1}];
this.form.skus = [{
logistics_sku_id: 0,
position: 1,
variants: [],
image: '',
model: '',
sku: '',
price: null,
quantity: null,
is_default: 1
}];
this.editing.isVariable = false;
}, 0);
}
@ -492,17 +572,17 @@
},
addAttribute() {
this.form.attributes.push({attribute:{id:'',name:''}, attribute_value: {id:'',name:''}})
this.form.attributes.push({attribute: {id: '', name: ''}, attribute_value: {id: '', name: ''}})
},
attributeQuerySearch(keyword, cb) {
$http.get('attributes/autocomplete?name=' + encodeURIComponent(keyword), null, {hload:true}).then((res) => {
$http.get('attributes/autocomplete?name=' + encodeURIComponent(keyword), null, {hload: true}).then((res) => {
cb(res.data);
})
},
attributeValueQuerySearch(keyword, cb, index) {
$http.get(`attributes/${this.form.attributes[index].attribute.id}/values/autocomplete?name=${encodeURIComponent(keyword)}`, null, {hload:true}).then((res) => {
$http.get(`attributes/${this.form.attributes[index].attribute.id}/values/autocomplete?name=${encodeURIComponent(keyword)}`, null, {hload: true}).then((res) => {
res.data.push({id: 'add', name: '{{ __('admin/attribute.add_attribute') }}'})
cb(res.data);
})
@ -586,7 +666,7 @@
// 规格值拖拽
swapSourceVariantValue(e, variantIndex) {
this.form.skus.forEach(function(sku) {
this.form.skus.forEach(function (sku) {
const oldIndex = parseInt(sku.variants[variantIndex]);
if (oldIndex == e.oldIndex) {
sku.variants[variantIndex] = e.newIndex.toString();
@ -601,6 +681,10 @@
this.remakeSkus()
},
// 国家信息改变
changeCountry(info){
this.country_list = info;
}
}
});
@ -666,8 +750,8 @@
}
});
});
</script lang="scss">
<style>
</script>
<style lang="scss">
.price_setting_by_num{
display: flex;
margin-left: 200px;

View File

@ -25,6 +25,8 @@ return [
'edit_address' => 'Edit address',
'address' => 'Address',
'choose_country' => 'Choose Country',
'search_placeholder' => 'Please enter the country name',
'empty_placeholder' => 'There is currently no data available. Please enter the country name in the input box to search!',
'zones' => 'Zones',
'choose_zones' => 'Choose Zones',
'enter_city' => 'Enter city',

View File

@ -15,7 +15,6 @@ return [
'customers_show' => '客户详情',
'customers_update' => '更新客户',
'customers_delete' => '删除客户',
'user_info' => '用户信息',
'address_management' => '地址管理',
'user_name' => '用户名',
@ -25,6 +24,8 @@ return [
'edit_address' => '编辑地址',
'address' => '地址',
'choose_country' => '选择国家',
'search_placeholder' => '请输入国家名称',
'empty_placeholder' => '暂无数据,请在输入框内输入国家名称进行搜索!',
'zones' => '省份',
'choose_zones' => '选择省份',
'enter_city' => '输入城市',

View File

@ -0,0 +1,228 @@
<style>
.countries-list{
margin-top: 10px;
width: 100%;
}
.countries-list .list-box{
border: 1px solid #e7e7e7;
display: inline-flex;
flex-direction: column;
flex-wrap: nowrap;
align-items: flex-start;
justify-content: center;
margin: 0 10px 10px 0;
border-radius: 5px;
padding: 0 10px;
min-width: 120px!important;
cursor: pointer;
user-select: none;
}
@media screen and (max-width: 400px) {
.countries-list .list-box{
--show-num-: 1;
width: 100%;
}
.countries-list .list-box{
margin-right: 0!important;
}
}
@media screen and (min-width: 400px) and (max-width: 600px) {
.countries-list .list-box{
--show-num-: 2;
width: calc((100% - (var(--show-num-) * 10px - 10px)) / var(--show-num-));
}
.countries-list .list-box:nth-child(2n){
margin-right: 0!important;
}
}
@media screen and (min-width: 600px) and (max-width: 800px) {
.countries-list .list-box{
--show-num-: 3;
width: calc((100% - (var(--show-num-) * 10px - 10px)) / var(--show-num-));
}
.countries-list .list-box:nth-child(3n){
margin-right: 0!important;
}
}
@media screen and (min-width: 800px) and (max-width: 1000px) {
.countries-list .list-box{
--show-num-: 4;
width: calc((100% - (var(--show-num-) * 10px - 10px)) / var(--show-num-));
}
.countries-list .list-box:nth-child(4n){
margin-right: 0!important;
}
}
@media screen and (min-width: 1000px) and (max-width: 1200px){
.countries-list .list-box{
--show-num-: 5;
width: calc((100% - (var(--show-num-) * 10px - 10px)) / var(--show-num-));
}
.countries-list .list-box:nth-child(5n){
margin-right: 0!important;
}
}
@media screen and (min-width: 1200px) and (max-width: 1400px){
.countries-list .list-box{
--show-num-: 6;
width: calc((100% - (var(--show-num-) * 10px - 10px)) / var(--show-num-));
}
.countries-list .list-box:nth-child(6n){
margin-right: 0!important;
}
}
@media screen and (min-width: 1400px) and (max-width: 1600px){
.countries-list .list-box{
--show-num-: 7;
width: calc((100% - (var(--show-num-) * 10px - 10px)) / var(--show-num-));
}
.countries-list .list-box:nth-child(7n){
margin-right: 0!important;
}
}
@media screen and (min-width: 1600px){
.countries-list .list-box{
--show-num-: 8;
width: calc((100% - (var(--show-num-) * 10px - 10px)) / var(--show-num-));
}
.countries-list .list-box:nth-child(8n){
margin-right: 0!important;
}
}
.countries-list .list-box .item-title{
font-size: 18px;
font-weight: bold;
color: #707070;
width: 100%;
height: 30px;
line-height: 30px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.countries-list .list-box .item-code{
font-size: 14px;
color: #656565;
height: 30px;
line-height: 30px;
}
.countries-list .list-box-active{
position: relative;
border-color: #409eff;
}
.countries-list .list-box-active .selected-content{
position: absolute;
bottom: 0;
right: 5px;
color: #409eff;
height: 20px;
}
.countries-list .list-box-active .selected-content i{
font-size: 20px;
display: inline-flex;
height: 20px;
flex-direction: row;
flex-wrap: nowrap;
align-items: flex-end;
}
</style>
<template id="select-countries">
<el-dialog
custom-class="mobileWidth"
title="{{ __('admin/customer.choose_country') }}"
:visible.sync="is_show"
width="80%" @close="closeSelectCountries">
{{--顶部内容--}}
<div class="top">
<input type="text" v-model="countries_name" class="form-control" placeholder="{{ __('admin/customer.search_placeholder') }}"/>
</div>
{{--国家列表--}}
<div class="countries-list" v-if="Object.keys(countries_list).length > 0">
<div :class="['list-box',{'list-box-active':isSelected(item)}]" v-for="(item,index) in countries_list" :title="item.name" @click="clickCountries(item)">
<div class="item-title">@{{ item.name }}</div>
<div class="item-code">@{{ item.code }}</div>
<div v-if="isSelected(item)" class="selected-content">
<i class="bi bi-check-all"></i>
</div>
</div>
</div>
<el-empty v-else description="{{ __('admin/customer.empty_placeholder') }}" />
</el-dialog>
</template>
<script>
Vue.component('select-countries', {
template: '#select-countries',
props: {
country_list: {
default: {},
type: [Object,Array]
},
},
data: function () {
return {
is_show: false,
countries_name: '',
countries_list: {},
}
},
watch:{
countries_name(){
this.getCountriesList();
}
},
computed: {},
beforeMount() {},
mounted(){
this.getCountriesList()
},
methods: {
// 显示国家选择器
showSelectCountries() {
this.is_show = true;
},
// 关闭国家选择器
closeSelectCountries() {
this.is_show = false;
},
// 国家列表
getCountriesList(){
let _this = this;
let url = `countries/autocomplete`;
let params = {
name: _this.countries_name,
page_size: 'all',
};
// 发起请求
$http.get(url, params, {hload: true}).then((res) => {
if(res.status === 'success'){
_this.countries_list = res.data || {};
}
})
},
// 判断:当前国家是否已经被选中
isSelected(item){
let isHas = false;
if (Object.values(this.country_list).findIndex(info => parseInt(info.id) === parseInt(item.id)) !== -1) {
isHas = true;
}
return isHas;
},
// 点击当前国家
clickCountries(item){
let _this = this;
let countryIds = Object.assign({}, _this.country_list);
// 判断:根据是否已经存在进行对应的存在 不存在-添加;存在-删除
if(_this.isSelected(item)) delete countryIds[item.id];// 存在-删除
else countryIds[item.id] = item;// 不存在 添加
_this.$message.success("{{ __('common.edit_success') }}");
_this.$emit('change-info',countryIds);
}
}
});
</script>