This commit is contained in:
pushuo 2022-08-01 16:00:45 +08:00
parent 95ac80ee36
commit 7280c7e3c2
13 changed files with 419 additions and 295 deletions

View File

@ -150,6 +150,86 @@ body {
min-height: 1000px;
}
.h-min-100 {
min-width: 100px;
}
.h-min-200 {
min-width: 200px;
}
.h-min-300 {
min-width: 300px;
}
.h-min-400 {
min-width: 400px;
}
.h-min-500 {
min-width: 500px;
}
.h-min-600 {
min-width: 600px;
}
.h-min-700 {
min-width: 700px;
}
.h-min-800 {
min-width: 800px;
}
.h-min-900 {
min-width: 900px;
}
.h-min-1000 {
min-width: 1000px;
}
.w-max-100 {
max-width: 100px;
}
.w-max-200 {
max-width: 200px;
}
.w-max-300 {
max-width: 300px;
}
.w-max-400 {
max-width: 400px;
}
.w-max-500 {
max-width: 500px;
}
.w-max-600 {
max-width: 600px;
}
.w-max-700 {
max-width: 700px;
}
.w-max-800 {
max-width: 800px;
}
.w-max-900 {
max-width: 900px;
}
.w-max-1000 {
max-width: 1000px;
}
.hp-100 {
height: 100px;
}
@ -567,13 +647,14 @@ hr.horizontal.dark {
.nav-tabs.nav-bordered .nav-item:last-child {
margin-right: 0;
}
.nav-tabs.nav-bordered .nav-item a {
.nav-tabs.nav-bordered .nav-item .nav-link {
color: #6c757d;
border: none;
padding: 0.7rem 0;
padding: 0 0.2rem 0.7rem;
}
.nav-tabs.nav-bordered .nav-item a.active {
.nav-tabs.nav-bordered .nav-item .nav-link.active {
color: #1a1a1a;
font-weight: bold;
background-color: transparent;
border-bottom: 2px solid #fd560f;
}

View File

@ -12296,7 +12296,7 @@ textarea.form-control-lg {
align-items: center;
justify-content: space-between;
padding: 1rem 1rem;
border-bottom: 1px solid #e2e2e2;
border-bottom: 1px solid #f4f4f4;
border-top-left-radius: calc(0.3rem - 1px);
border-top-right-radius: calc(0.3rem - 1px);
}
@ -12323,7 +12323,7 @@ textarea.form-control-lg {
align-items: center;
justify-content: flex-end;
padding: 0.75rem;
border-top: 1px solid #e2e2e2;
border-top: 1px solid #f4f4f4;
border-bottom-right-radius: calc(0.3rem - 1px);
border-bottom-left-radius: calc(0.3rem - 1px);
}
@ -13581,7 +13581,7 @@ textarea.form-control-lg {
}
.border {
border: 1px solid #e2e2e2 !important;
border: 1px solid #f4f4f4 !important;
}
.border-0 {
@ -13589,7 +13589,7 @@ textarea.form-control-lg {
}
.border-top {
border-top: 1px solid #e2e2e2 !important;
border-top: 1px solid #f4f4f4 !important;
}
.border-top-0 {
@ -13597,7 +13597,7 @@ textarea.form-control-lg {
}
.border-end {
border-right: 1px solid #e2e2e2 !important;
border-right: 1px solid #f4f4f4 !important;
}
.border-end-0 {
@ -13605,7 +13605,7 @@ textarea.form-control-lg {
}
.border-bottom {
border-bottom: 1px solid #e2e2e2 !important;
border-bottom: 1px solid #f4f4f4 !important;
}
.border-bottom-0 {
@ -13613,7 +13613,7 @@ textarea.form-control-lg {
}
.border-start {
border-left: 1px solid #e2e2e2 !important;
border-left: 1px solid #f4f4f4 !important;
}
.border-start-0 {

View File

@ -2067,6 +2067,7 @@ __webpack_require__.r(__webpack_exports__);
window.$http = _js_http__WEBPACK_IMPORTED_MODULE_0__["default"];
var base = document.querySelector('base').href;
var asset = document.querySelector('meta[name="asset"]').content;
var editor_language = document.querySelector('meta[name="editor_language"]').content;
$(document).on('click', '.open-file-manager', function (event) {
var $this = $(this);
layer.open({
@ -2120,8 +2121,39 @@ $(document).ready(function ($) {
}
}
});
tinymceInit();
});
function tinymceInit() {
if (typeof tinymce == 'undefined') {
return;
}
tinymce.init({
selector: '.tinymce',
language: editor_language,
branding: false,
height: 400,
plugins: "link lists fullscreen table hr wordcount image imagetools code",
menubar: "",
toolbar: "undo redo | toolbarImageButton | bold italic underline strikethrough | forecolor backcolor | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | formatpainter removeformat | charmap emoticons | preview | template link anchor table toolbarImageUrlButton | fullscreen code",
// contextmenu: "link image imagetools table",
toolbar_items_size: 'small',
image_caption: true,
// imagetools_toolbar: 'imageoptions',
toolbar_mode: 'wrap',
font_formats: "微软雅黑='Microsoft YaHei';黑体=黑体;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Georgia=georgia,palatino;Helvetica=helvetica;Times New Roman=times new roman,times;Verdana=verdana,geneva",
fontsize_formats: "10px 12px 14px 18px 24px 36px",
relative_urls: true,
setup: function setup(ed) {// ed.on('change', function(e) {
// if (e.target.targetElm.dataset.key) {
// app.form[e.target.targetElm.dataset.key] = ed.getContent()
// }
// });
}
});
}
/***/ }),
/***/ "./resources/js/http.js":

View File

@ -36,19 +36,19 @@ hr.horizontal.dark {
margin-right: 0;
}
a {
.nav-link {
color: #6c757d;
border: none;
padding: 0.7rem 0;
}
padding: 0 .2rem 0.7rem;
a.active {
// color: $primary;
// color: #12263f;
color: #1a1a1a;
// font-weight: bold;
background-color: transparent;
border-bottom: 2px solid $primary;
&.active {
// color: $primary;
// color: #12263f;
color: #1a1a1a;
font-weight: bold;
background-color: transparent;
border-bottom: 2px solid $primary;
}
}
}
}

View File

@ -43,6 +43,20 @@ body {
}
}
// 生成 100 200 300 ... 1000 的最小宽度
@for $i from 1 through 10 {
.h-min-#{$i}00 {
min-width: #{$i}00px;
}
}
// 生成 100 200 300 ... 1000 的最大宽度
@for $i from 1 through 10 {
.w-max-#{$i}00 {
max-width: #{$i}00px;
}
}
// 生成 100 200 300 ... 1000 的高度
@for $i from 1 through 10 {
.hp-#{$i}00 {

View File

@ -21,7 +21,7 @@ $form-floating-padding-y: .9rem;
$form-floating-height: 50px;
$btn-focus-width: 0;
$table-border-color: #e9ecef;
$border-color: #e2e2e2;
$border-color: #f4f4f4;
$input-border-color: #e2e2e2;
$badge-border-radius: 2px;
$text-muted: #95aac9;

View File

@ -2,6 +2,7 @@ import http from "../../../js/http";
window.$http = http;
const base = document.querySelector('base').href;
const asset = document.querySelector('meta[name="asset"]').content;
const editor_language = document.querySelector('meta[name="editor_language"]').content;
$(document).on('click', '.open-file-manager', function(event) {
const $this = $(this);
@ -48,4 +49,38 @@ $(document).ready(function ($) {
}
},
});
tinymceInit()
});
function tinymceInit() {
if (typeof tinymce == 'undefined') {
return;
}
tinymce.init({
selector: '.tinymce',
language: editor_language,
branding: false,
height: 400,
plugins: "link lists fullscreen table hr wordcount image imagetools code",
menubar: "",
toolbar: "undo redo | toolbarImageButton | bold italic underline strikethrough | forecolor backcolor | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | formatpainter removeformat | charmap emoticons | preview | template link anchor table toolbarImageUrlButton | fullscreen code",
// contextmenu: "link image imagetools table",
toolbar_items_size: 'small',
image_caption: true,
// imagetools_toolbar: 'imageoptions',
toolbar_mode: 'wrap',
font_formats:
"微软雅黑='Microsoft YaHei';黑体=黑体;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Georgia=georgia,palatino;Helvetica=helvetica;Times New Roman=times new roman,times;Verdana=verdana,geneva",
fontsize_formats: "10px 12px 14px 18px 24px 36px",
relative_urls : true,
setup:function(ed) {
// ed.on('change', function(e) {
// if (e.target.targetElm.dataset.key) {
// app.form[e.target.targetElm.dataset.key] = ed.getContent()
// }
// });
}
});
}

View File

@ -1,6 +1,6 @@
<div class="row g-3 mb-3">
<label for="" class="wp-200 col-form-label text-end">{{ $title ?? '' }}</label>
<div class="col-auto">
<div class="col-auto wp-200-">
{{ $slot }}
</div>
</div>

View File

@ -6,6 +6,7 @@
<base href="{{ $admin_base_url }}">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="asset" content="{{ asset('/') }}">
<meta name="editor_language" content="{{ current_language_code() }}">
<script src="{{ asset('vendor/vue/2.6.12/vue.js') }}"></script>
<script src="{{ asset('vendor/element-ui/2.6.2/js.js') }}"></script>
<script src="{{ asset('vendor/jquery/jquery-3.6.0.min.js') }}"></script>

View File

@ -29,7 +29,7 @@
</tr>
</thead>
<tbody>
<tr v-for="tax, index in tax_classes" :key="index">
<tr v-for="tax, index in admin_users" :key="index">
<td>@{{ tax.id }}</td>
<td>@{{ tax.name }}</td>
<td>@{{ tax.email }}</td>
@ -43,7 +43,7 @@
</tbody>
</table>
{{-- {{ $tax_classes->links('admin::vendor/pagination/bootstrap-4') }} --}}
{{-- {{ $admin_users->links('admin::vendor/pagination/bootstrap-4') }} --}}
</div>
<el-dialog title="创建税费" :visible.sync="dialog.show" width="700px"
@ -104,7 +104,7 @@
el: '#tax-classes-app',
data: {
tax_classes: @json($admin_users ?? []),
admin_users: @json($admin_users ?? []),
source: {
all_tax_rates: @json($all_tax_rates ?? []),
@ -143,7 +143,7 @@
this.dialog.index = index
if (type == 'edit') {
let tax = this.tax_classes[index];
let tax = this.admin_users[index];
this.dialog.form = {
id: tax.id,
@ -168,7 +168,7 @@
addFormSubmit(form) {
const self = this;
const type = this.dialog.type == 'add' ? 'post' : 'put';
const url = this.dialog.type == 'add' ? 'tax_classes' : 'tax_classes/' + this.dialog.form.id;
const url = this.dialog.type == 'add' ? 'admin_users' : 'admin_users/' + this.dialog.form.id;
this.$refs[form].validate((valid) => {
if (!valid) {
@ -179,9 +179,9 @@
$http[type](url, this.dialog.form).then((res) => {
this.$message.success(res.message);
if (this.dialog.type == 'add') {
this.tax_classes.push(res.data)
this.admin_users.push(res.data)
} else {
this.tax_classes[this.dialog.index] = res.data
this.admin_users[this.dialog.index] = res.data
}
this.dialog.show = false
@ -196,9 +196,9 @@
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
$http.delete('tax_classes/' + id).then((res) => {
$http.delete('admin_users/' + id).then((res) => {
this.$message.success(res.message);
self.tax_classes.splice(index, 1)
self.admin_users.splice(index, 1)
})
}).catch(()=>{})
},

View File

@ -15,21 +15,21 @@
<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)">添加</button>
<button type="button" class="btn btn-primary" @click="checkedCreate('add', null)">创建用户</button>
</div>
<table class="table">
<thead>
<tr>
<th>#</th>
<th>名称</th>
<th>描述</th>
<th>账号名称</th>
<th>邮箱</th>
<th>创建时间</th>
<th>修改时间</th>
<th class="text-end">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="tax, index in tax_classes" :key="index">
<tr v-for="tax, index in admin_users" :key="index">
<td>@{{ tax.id }}</td>
<td>@{{ tax.name }}</td>
<td>@{{ tax.email }}</td>
@ -43,52 +43,27 @@
</tbody>
</table>
{{-- {{ $tax_classes->links('admin::vendor/pagination/bootstrap-4') }} --}}
{{-- {{ $admin_users->links('admin::vendor/pagination/bootstrap-4') }} --}}
</div>
<el-dialog title="创建税费" :visible.sync="dialog.show" width="700px"
<el-dialog title="用户" :visible.sync="dialog.show" width="600px"
@close="closeCustomersDialog('form')" :close-on-click-modal="false">
<el-form ref="form" :rules="rules" :model="dialog.form" label-width="100px">
<el-form-item label="税类名称" prop="title">
<el-input v-model="dialog.form.title" placeholder="税类名称"></el-input>
<el-form-item label="账号名称" prop="name">
<el-input v-model="dialog.form.name" placeholder="账号名称"></el-input>
</el-form-item>
<el-form-item label="描述" prop="description">
<el-input v-model="dialog.form.description" placeholder="描述"></el-input>
<el-form-item label="邮箱" prop="email">
<el-input v-model="dialog.form.email" placeholder="邮箱"></el-input>
</el-form-item>
<el-form-item label="规则">
<table class="table table-bordered" style="line-height: 1.6;">
<thead>
<tr>
<th>税率</th>
<th>基于</th>
<th>优先级</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="rule, index in dialog.form.tax_rules" :key="index">
<td>
<el-select v-model="rule.tax_rate_id" size="mini" placeholder="请选择">
<el-option v-for="tax in source.all_tax_rates" :key="tax.id" :label="tax.name" :value="tax.id"></el-option>
</el-select>
</td>
<td>
<el-select v-model="rule.based" size="mini" placeholder="请选择">
<el-option v-for="base in source.bases" :key="base" :label="base" :value="base"></el-option>
</el-select>
</td>
<td width="80px"><el-input v-model="rule.priority" size="mini" placeholder="优先级"></el-input></td>
<td>
<button class="btn btn-outline-danger btn-sm ml-1" type="button" @click="deleteRates(index)">删除</button>
</td>
</tr>
</tbody>
</table>
<el-button type="primary" icon="el-icon-plus" size="small" plain @click="addRates">添加规则</el-button>
<el-form-item label="角色" prop="roles">
<el-checkbox-group v-model="dialog.form.roles">
<el-checkbox v-for="roles, index in source.roles" :label="roles.id">@{{roles.name}}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item class="mt-5">
<el-button type="primary" @click="addFormSubmit('form')">保存</el-button>
<el-button @click="closeCustomersDialog('form')">取消</el-button>
@ -104,11 +79,11 @@
el: '#tax-classes-app',
data: {
tax_classes: @json($admin_users ?? []),
admin_users: @json($admin_users ?? []),
source: {
all_tax_rates: @json($all_tax_rates ?? []),
bases: @json($bases ?? []),
roles: @json($admin_roles ?? [])
},
dialog: {
@ -117,25 +92,19 @@
type: 'add',
form: {
id: null,
title: '',
description: '',
tax_rules: [],
name: '',
email: '',
roles: [],
},
},
rules: {
title: [{required: true,message: '请输入税类名称',trigger: 'blur'}, ],
description: [{required: true,message: '请输入描述',trigger: 'blur'}, ],
name: [{required: true,message: '请输入账号名称',trigger: 'blur'}, ],
email: [{required: true,message: '请输入邮箱',trigger: 'blur'}, ],
roles: [{type: 'array', required: true, message: '请至少选择一个角色', trigger: 'change'}],
}
},
beforeMount() {
// this.source.languages.forEach(e => {
// this.$set(this.dialog.form.name, e.code, '')
// this.$set(this.dialog.form.description, e.code, '')
// })
},
methods: {
checkedCreate(type, index) {
this.dialog.show = true
@ -143,32 +112,20 @@
this.dialog.index = index
if (type == 'edit') {
let tax = this.tax_classes[index];
let tax = this.admin_users[index];
this.dialog.form = {
id: tax.id,
title: tax.title,
description: tax.description,
tax_rules: tax.tax_rules,
email: tax.email,
}
}
},
addRates() {
const tax_rate_id = this.source.all_tax_rates[0]?.id || 0;
const based = this.source.bases[0] || '';
this.dialog.form.tax_rules.push({tax_rate_id, based, priority: ''})
},
deleteRates(index) {
this.dialog.form.tax_rules.splice(index, 1)
},
addFormSubmit(form) {
const self = this;
const type = this.dialog.type == 'add' ? 'post' : 'put';
const url = this.dialog.type == 'add' ? 'tax_classes' : 'tax_classes/' + this.dialog.form.id;
const url = this.dialog.type == 'add' ? 'admin_users' : 'admin_users/' + this.dialog.form.id;
this.$refs[form].validate((valid) => {
if (!valid) {
@ -179,9 +136,9 @@
$http[type](url, this.dialog.form).then((res) => {
this.$message.success(res.message);
if (this.dialog.type == 'add') {
this.tax_classes.push(res.data)
this.admin_users.push(res.data)
} else {
this.tax_classes[this.dialog.index] = res.data
this.admin_users[this.dialog.index] = res.data
}
this.dialog.show = false
@ -191,21 +148,21 @@
deleteCustomer(id, index) {
const self = this;
this.$confirm('确定要删除税类码', '提示', {
this.$confirm('确定要删除用户吗', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
$http.delete('tax_classes/' + id).then((res) => {
$http.delete('admin_users/' + id).then((res) => {
this.$message.success(res.message);
self.tax_classes.splice(index, 1)
self.admin_users.splice(index, 1)
})
}).catch(()=>{})
},
closeCustomersDialog(form) {
Object.keys(this.dialog.form).forEach(key => this.dialog.form[key] = '')
this.dialog.form.tax_rules = []
this.dialog.form.roles = [];
this.dialog.show = false
}
}

View File

@ -1,226 +1,230 @@
@extends('admin::layouts.master')
@section('title', '商品详情')
@section('body-class', 'page-product-form')
@push('header')
<script src="{{ asset('vendor/vue/Sortable.min.js') }}"></script>
<script src="{{ asset('vendor/vue/vuedraggable.js') }}"></script>
<script src="{{ asset('vendor/tinymce/5.9.1/tinymce.min.js') }}"></script>
@endpush
@section('content')
<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" >基础信息</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-descriptions" type="button">商品详情</button>
</li>
</ul>
<div class="card">
{{-- <div class="card-header"><h6 class="card-title">基础信息</h6></div> --}}
<div class="card-body">
<h5 class="border-bottom pb-3 mb-4">基础信息</h5>
<form action="{{ $product->id ? admin_route('products.update', $product) : admin_route('products.store') }}"
method="POST" id="app">
@csrf
@method($product->id ? 'PUT' : 'POST')
<input type="hidden" name="_redirect" value="{{ $_redirect }}" />
@foreach (locales() as $index => $locale)
{{-- <input type="hidden" name="descriptions[{{ $index }}][locale]" value="{{ $locale['code'] }}"> --}}
@endforeach
<x-admin-form-input-locale name="descriptions.*.name" title="名称" :value="$descriptions" required />
{{-- <x-admin-form-input name="image" title="主图" :value="old('image', $product->image ?? '')" /> --}}
<x-admin::form.row title="图片">
<div class="product-images d-flex flex-wrap">
<div v-for="image, index in form.images" :key="index" class="wh-80 border d-flex justify-content-center align-items-center me-2">
<img :src="thumbnail(image)" class="img-fluid">
<input type="hidden" name="images[]" :value="image">
</div>
<div class="set-product-img wh-80" @click="addProductImages"><i class="bi bi-plus fs-1 text-muted"></i></div>
</div>
<div class="help-text mb-1 mt-2">第一张图片将作为商品主图,支持同时上传多张图片,多张图片之间可随意调整位置;</div>
<div class="help-text">开启多规格并且多规格配置了图片时,这里的图片将作为多规格的公用图片,展示在其后面</div>
</x-admin::form.row>
{{-- <div class="set-product-img">
@if ($product->image)
<div>
<img src="{{ old('images', $product->images ?? '') }}" class="img-fluid">
</div>
@else
<i class="bi bi-plus fs-1 text-muted"></i>
@endif
</div>
<input type="hidden" value="{{ old('images', $product->images ?? '') }}" name="skus[0][image]">
--}}
<x-admin-form-input name="video" title="视频" :value="old('video', $product->video ?? '')" />
<x-admin-form-input name="position" title="排序" :value="old('position', $product->position ?? '')" />
<x-admin-form-switch name="active" title="状态" :value="old('active', $product->active ?? 1)" />
<x-admin::form.row title="分类">
@foreach ($source['categories'] as $_category)
<div class="form-check">
<input class="form-check-input" type="checkbox" name="categories[]" value="{{ $_category->id }}"
id="category-{{ $_category->id }}" {{ in_array($_category->id, $category_ids) ? 'checked' : '' }}>
<label class="form-check-label" for="category-{{ $_category->id }}">
{{ $_category->name }}
</label>
</div>
@endforeach
</x-admin::form.row>
<div>
<h5 class="border-bottom pb-3 mb-4">商品库存</h5>
<x-admin::form.row title="启用多规格">
<el-switch v-model="editing.isVariable"></el-switch>
</x-admin::form.row>
<input type="hidden" name="variables" :value="JSON.stringify(form.variables)">
<div class="row g-3 mb-3" v-if="editing.isVariable">
<label for="" class="wp-200 col-form-label text-end"></label>
<div class="col-auto wp-200-">
<div class="selectable-variants">
<div>
<draggable :list="source.variables" :options="{animation: 100}">
<div v-for="(variant, variantIndex) in source.variables" :id="'selectable-variant-' + variantIndex">
<div class="title">
<div>
<b>@{{ variant.name[current_language_code] }}</b>
<el-link type="primary" @click="modalVariantOpenButtonClicked(variantIndex, null)">编辑</el-link>
<el-link type="danger" class="ms-2" @click="removeSourceVariant(variantIndex)">移除</el-link>
</div>
<div>
<el-checkbox v-model="variant.isImage" border size="mini" class="me-2 bg-white">添加规格图片</el-checkbox>
<el-button type="primary" plain size="mini" @click="modalVariantOpenButtonClicked(variantIndex, -1)">Add value</el-button>
</div>
</div>
<draggable
element="div"
@start="isMove = true"
v-if="variant.values.length"
class="variants-wrap"
@update="(e) => {swapSourceVariantValue(e, variantIndex)}"
@end="isMove = false"
ghost-class="dragabble-ghost"
:list="variant.values"
:options="{animation: 100}"
>
<div v-for="(value, value_index) in variant.values" :key="value_index" class="variants-item" @dblclick="modalVariantOpenButtonClicked(variantIndex, value_index)">
{{-- <div class="value-img" v-if="variant.isImage"> --}}
{{-- <a href="" :id="'value-img-' + i + '-' + value_index" data-toggle="image" data-no-preview> --}}
{{-- <img :src="thumbnail(value.image)" class="img-responsive" /> --}}
{{-- </a> --}}
{{-- </div> --}}
<div class="open-file-manager variant-value-img" v-if="variant.isImage">
<div>
<img :src="thumbnail(value.image)" class="img-fluid">
</div>
</div>
<input type="hidden" v-model="value.image">
<div class="btn-remove" @click="removeSourceVariantValue(variantIndex, value_index)"><i class="el-icon-error"></i></div>
<div class="name">
@{{ value.name[current_language_code] }}
</div>
</div>
</draggable>
<div v-else>
<div class="p-2" @click="modalVariantOpenButtonClicked(variantIndex, -1)">请添加 Value</div>
</div>
</div>
</draggable>
<el-button type="primary" size="small" @click="modalVariantOpenButtonClicked(-1, null)" class="btn btn-xs mr-1 mb-1">Add variant</el-button>
<div class="tab-content">
<div class="tab-pane fade show active" id="tab-basic">
<h6 class="border-bottom pb-3 mb-4">数据</h6>
<x-admin-form-input-locale name="descriptions.*.name" title="名称" :value="$descriptions" required />
<x-admin::form.row title="图片">
<div class="product-images d-flex flex-wrap">
<div v-for="image, index in form.images" :key="index" class="wh-80 border d-flex justify-content-center align-items-center me-2">
<img :src="thumbnail(image)" class="img-fluid">
<input type="hidden" name="images[]" :value="image">
</div>
<div class="set-product-img wh-80" @click="addProductImages"><i class="bi bi-plus fs-1 text-muted"></i></div>
</div>
<div class="help-text mb-1 mt-2">第一张图片将作为商品主图,支持同时上传多张图片,多张图片之间可随意调整位置;</div>
<div class="help-text">开启多规格并且多规格配置了图片时,这里的图片将作为多规格的公用图片,展示在其后面</div>
</x-admin::form.row>
<x-admin-form-input name="video" title="视频" :value="old('video', $product->video ?? '')" />
<x-admin-form-input name="position" title="排序" :value="old('position', $product->position ?? '')" />
<x-admin-form-switch name="active" title="状态" :value="old('active', $product->active ?? 1)" />
<div v-if="form.skus.length && form.variables.length" class="mt-3">
<table class="table table-bordered table-hover">
<thead>
<th v-for="(variant, index) in form.variables" :key="'pv-header-' + index">
@{{ variant.name[current_language_code] || 'No name' }}
</th>
<th width="106px">image</th>
<th>model</th>
<th>sku</th>
<th>price</th>
<th>orgin price</th>
<th>cost price</th>
<th>quantity</th>
</thead>
<tbody>
<tr v-for="(sku, skuIndex) in form.skus" :key="skuIndex">
<template v-for="(variantValueIndex, j) in sku.variants">
<td v-if="skuIndex % variantValueRepetitions[j] == 0" :key="'pvv' + skuIndex + '-' + j"
:rowspan="variantValueRepetitions[j]">
<span>@{{ form.variables[j].values[variantValueIndex].name[current_language_code] || 'No name' }}</span>
</td>
</template>
<td>
<div class="product-images d-flex flex-wrap" style="margin-right: -8px">
<div v-for="image, index in sku.images" :key="index" class="wh-40 border d-flex justify-content-center align-items-center me-2 mb-2">
<img :src="thumbnail(image)" class="img-fluid">
<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>
<x-admin::form.row title="分类">
@foreach ($source['categories'] as $_category)
<div class="form-check">
<input class="form-check-input" type="checkbox" name="categories[]" value="{{ $_category->id }}"
id="category-{{ $_category->id }}" {{ in_array($_category->id, $category_ids) ? 'checked' : '' }}>
<label class="form-check-label" for="category-{{ $_category->id }}">
{{ $_category->name }}
</label>
</div>
@endforeach
</x-admin::form.row>
{{-- <div class="open-file-manager variants-producr-img">
<div>
<h5 class="border-bottom pb-3 mb-4">商品库存</h5>
<x-admin::form.row title="启用多规格">
<el-switch v-model="editing.isVariable" class="mt-2"></el-switch>
</x-admin::form.row>
<input type="hidden" name="variables" :value="JSON.stringify(form.variables)">
<div class="row g-3 mb-3" v-if="editing.isVariable">
<label for="" class="wp-200 col-form-label text-end"></label>
<div class="col-auto wp-200-">
<div class="selectable-variants">
<div>
<draggable :list="source.variables" :options="{animation: 100}">
<div v-for="(variant, variantIndex) in source.variables" :id="'selectable-variant-' + variantIndex">
<div class="title">
<div>
<img :src="thumbnail(sku.image)" class="img-fluid">
<b>@{{ variant.name[current_language_code] }}</b>
<el-link type="primary" @click="modalVariantOpenButtonClicked(variantIndex, null)">编辑</el-link>
<el-link type="danger" class="ms-2" @click="removeSourceVariant(variantIndex)">移除</el-link>
</div>
<div>
<el-checkbox v-model="variant.isImage" border size="mini" class="me-2 bg-white">添加规格图片</el-checkbox>
<el-button type="primary" plain size="mini" @click="modalVariantOpenButtonClicked(variantIndex, -1)">Add value</el-button>
</div>
</div>
<input type="hidden" class="form-control" v-model="sku.image" :name="'skus[' + skuIndex + '][image]'"
placeholder="image"> --}}
<draggable
element="div"
@start="isMove = true"
v-if="variant.values.length"
class="variants-wrap"
@update="(e) => {swapSourceVariantValue(e, variantIndex)}"
@end="isMove = false"
ghost-class="dragabble-ghost"
:list="variant.values"
:options="{animation: 100}"
>
<div v-for="(value, value_index) in variant.values" :key="value_index" class="variants-item" @dblclick="modalVariantOpenButtonClicked(variantIndex, value_index)">
{{-- <div class="value-img" v-if="variant.isImage"> --}}
{{-- <a href="" :id="'value-img-' + i + '-' + value_index" data-toggle="image" data-no-preview> --}}
{{-- <img :src="thumbnail(value.image)" class="img-responsive" /> --}}
{{-- </a> --}}
{{-- </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"
:name="'skus[' + skuIndex + '][variants][' + j + ']'" :value="variantValueIndex">
</td>
<td><input type="text" class="form-control" v-model="sku.model" :name="'skus[' + skuIndex + '][model]'"
placeholder="model"></td>
<td><input type="text" class="form-control" v-model="sku.sku" :name="'skus[' + skuIndex + '][sku]'" placeholder="sku">
</td>
<td><input type="text" class="form-control" v-model="sku.price" :name="'skus[' + skuIndex + '][price]'"
placeholder="price"></td>
<td><input type="text" class="form-control" v-model="sku.origin_price" :name="'skus[' + skuIndex + '][origin_price]'"
placeholder="origin_price"></td>
<td><input type="text" class="form-control" v-model="sku.cost_price" :name="'skus[' + skuIndex + '][cost_price]'"
placeholder="cost_price">
</td>
<td><input type="text" class="form-control" v-model="sku.quantity" :name="'skus[' + skuIndex + '][quantity]'"
placeholder="quantity"></td>
</tr>
</tbody>
</table>
<div class="open-file-manager variant-value-img" v-if="variant.isImage">
<div>
<img :src="thumbnail(value.image)" class="img-fluid">
</div>
</div>
<input type="hidden" v-model="value.image">
<div class="btn-remove" @click="removeSourceVariantValue(variantIndex, value_index)"><i class="el-icon-error"></i></div>
<div class="name">
@{{ value.name[current_language_code] }}
</div>
</div>
</draggable>
<div v-else>
<div class="p-2" @click="modalVariantOpenButtonClicked(variantIndex, -1)">请添加 Value</div>
</div>
</div>
</draggable>
<el-button type="primary" plain size="small" @click="modalVariantOpenButtonClicked(-1, null)" class="btn btn-xs mr-1 mb-1">Add variant</el-button>
</div>
<div v-if="form.skus.length && form.variables.length" class="mt-3">
<table class="table table-bordered table-hover">
<thead>
<th v-for="(variant, index) in form.variables" :key="'pv-header-' + index">
@{{ variant.name[current_language_code] || 'No name' }}
</th>
<th width="106px">image</th>
<th>model</th>
<th>sku</th>
<th>price</th>
<th>orgin price</th>
<th>cost price</th>
<th>quantity</th>
</thead>
<tbody>
<tr v-for="(sku, skuIndex) in form.skus" :key="skuIndex">
<template v-for="(variantValueIndex, j) in sku.variants">
<td v-if="skuIndex % variantValueRepetitions[j] == 0" :key="'pvv' + skuIndex + '-' + j"
:rowspan="variantValueRepetitions[j]">
<span>@{{ form.variables[j].values[variantValueIndex].name[current_language_code] || 'No name' }}</span>
</td>
</template>
<td>
<div class="product-images d-flex flex-wrap" style="margin-right: -8px">
<div v-for="image, index in sku.images" :key="index" class="wh-40 border d-flex justify-content-center align-items-center me-2 mb-2">
<img :src="thumbnail(image)" class="img-fluid">
<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>
<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"
:name="'skus[' + skuIndex + '][variants][' + j + ']'" :value="variantValueIndex">
</td>
<td><input type="text" class="form-control" v-model="sku.model" :name="'skus[' + skuIndex + '][model]'"
placeholder="model"></td>
<td><input type="text" class="form-control" v-model="sku.sku" :name="'skus[' + skuIndex + '][sku]'" placeholder="sku">
</td>
<td><input type="text" class="form-control" v-model="sku.price" :name="'skus[' + skuIndex + '][price]'"
placeholder="price"></td>
<td><input type="text" class="form-control" v-model="sku.origin_price" :name="'skus[' + skuIndex + '][origin_price]'"
placeholder="origin_price"></td>
<td><input type="text" class="form-control" v-model="sku.cost_price" :name="'skus[' + skuIndex + '][cost_price]'"
placeholder="cost_price">
</td>
<td><input type="text" class="form-control" v-model="sku.quantity" :name="'skus[' + skuIndex + '][quantity]'"
placeholder="quantity"></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<template v-if="!editing.isVariable">
<input type="hidden" value="{{ old('skus.0.image', $product->skus[0]->image ?? '') }}" name="skus[0][image]">
<x-admin-form-input name="skus[0][model]" title="model" :value="old('skus.0.model', $product->skus[0]->model ?? '')" />
<x-admin-form-input name="skus[0][sku]" title="sku" :value="old('skus.0.sku', $product->skus[0]->sku ?? '')" />
<x-admin-form-input name="skus[0][price]" title="price" :value="old('skus.0.price', $product->skus[0]->price ?? '')" />
<x-admin-form-input name="skus[0][origin_price]" title="origin_price" :value="old('skus.0.origin_price', $product->skus[0]->origin_price ?? '')" />
<x-admin-form-input name="skus[0][cost_price]" title="cost_price" :value="old('skus.0.cost_price', $product->skus[0]->cost_price ?? '')" />
<x-admin-form-input name="skus[0][quantity]" title="quantity" :value="old('skus.0.quantity', $product->skus[0]->quantity ?? '')" />
<input type="hidden" name="skus[0][variants]" placeholder="variants" value="">
<input type="hidden" name="skus[0][position]" placeholder="position" value="0">
<input type="hidden" name="skus[0][is_default]" placeholder="is_default" value="1">
</template>
</div>
</div>
<div class="tab-pane fade" id="tab-descriptions">
<h6 class="border-bottom pb-3 mb-4">商品详情</h6>
<x-admin::form.row title="商品详情">
<template v-if="!editing.isVariable">
{{-- <x-admin::form.row title="图片">
<div class="open-file-manager set-product-img">
<div>
<img src="{{ 'catalog/' . old('skus.0.image', $product->skus[0]->image ?? '') }}" class="img-fluid">
<img src="{{ old('skus.0.image', $product->skus[0]->image ?? '') }}" class="img-fluid">
</div>
<ul class="nav nav-tabs mb-3" role="tablist">
@foreach ($languages as $language)
<li class="nav-item" role="presentation">
<button class="nav-link {{ $loop->first ? 'active' : '' }}" data-bs-toggle="tab" data-bs-target="#tab-descriptions-{{ $language->code }}" type="button" >{{ $language->code }}</button>
</li>
@endforeach
</ul>
<div class="tab-content">
@foreach ($languages as $language)
<div class="tab-pane fade {{ $loop->first ? 'show active' : '' }}" id="tab-descriptions-{{ $language->code }}">
<textarea name="descriptions[{{ $language->code }}]" class="form-control tinymce">
{{ old('descriptions', $product->descriptions[$language->code] ?? '') }}
</textarea>
</div>
@endforeach
</div>
<input type="hidden" value="{{ old('skus.0.image', $product->skus[0]->image ?? '') }}" name="skus[0][image]">
</x-admin::form.row> --}}
<input type="hidden" value="{{ old('skus.0.image', $product->skus[0]->image ?? '') }}" name="skus[0][image]">
<x-admin-form-input name="skus[0][model]" title="model" :value="old('skus.0.model', $product->skus[0]->model ?? '')" />
<x-admin-form-input name="skus[0][sku]" title="sku" :value="old('skus.0.sku', $product->skus[0]->sku ?? '')" />
<x-admin-form-input name="skus[0][price]" title="price" :value="old('skus.0.price', $product->skus[0]->price ?? '')" />
<x-admin-form-input name="skus[0][origin_price]" title="origin_price" :value="old('skus.0.origin_price', $product->skus[0]->origin_price ?? '')" />
<x-admin-form-input name="skus[0][cost_price]" title="cost_price" :value="old('skus.0.cost_price', $product->skus[0]->cost_price ?? '')" />
<x-admin-form-input name="skus[0][quantity]" title="quantity" :value="old('skus.0.quantity', $product->skus[0]->quantity ?? '')" />
<input type="hidden" name="skus[0][variants]" placeholder="variants" value="">
<input type="hidden" name="skus[0][position]" placeholder="position" value="0">
<input type="hidden" name="skus[0][is_default]" placeholder="is_default" value="1">
</template>
</x-admin::form.row>
</div>
</div>
<x-admin::form.row title="">
<button type="submit" class="btn btn-primary">Save</button>
<button type="submit" class="btn btn-primary">保存</button>
</x-admin::form.row>
<el-dialog

View File

@ -100,7 +100,7 @@
<input type="checkbox" :value="item.id" v-model="selected" />
</td>
<td>@{{ item.id }}</td>
<td><div class="wh-70"><img :src="item.image" class="img-fluid"></div></td>
<td><div class="wh-60"><img :src="item.images[0] || 'image/placeholder.png'" class="img-fluid"></div></td>
<td>@{{ item.name || '无名称' }}</td>
<td>@{{ item.price_formatted }}</td>
<td>@{{ item.created_at }}</td>