Skip to content

Commit 39beb63

Browse files
committed
feat(admin): 添加多图上传功能
- 在 Layui.php 中实现多图上传的 HTML 结构和 JavaScript 逻辑 - 新增 muti-upload.css 文件用于多图上传的样式 - 在 TableController 中集成多图上传功能 - 更新相关模板文件以支持多图上传
1 parent 84ec707 commit 39beb63

4 files changed

Lines changed: 210 additions & 49 deletions

File tree

src/plugin/admin/app/common/Layui.php

Lines changed: 138 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
namespace plugin\admin\app\common;
34

45
use support\exception\BusinessException;
@@ -45,19 +46,19 @@ public function js($indent = 0): string
4546
*/
4647
protected function options($options): array
4748
{
48-
array_walk_recursive($options, function(&$item, $key){
49+
array_walk_recursive($options, function (&$item, $key) {
4950
if (is_string($item)) {
5051
$item = htmlspecialchars($item);
5152
if ($key === 'url') {
5253
$item = str_replace('&amp;', '&', $item);
5354
}
5455
}
5556
});
56-
$field = $options['field']??'';
57+
$field = $options['field'] ?? '';
5758
$props = !empty($options['props']) ? $options['props'] : [];
58-
$verify_string = !empty($props['lay-verify']) ? ' lay-verify="'.$props['lay-verify'].'"' : '';
59+
$verify_string = !empty($props['lay-verify']) ? ' lay-verify="' . $props['lay-verify'] . '"' : '';
5960
$required_string = strpos($verify_string, 'required') ? ' required' : '';
60-
$label = !empty($options['label']) ? '<label class="layui-form-label'.$required_string.'">'.$options['label'].'</label>' : '';
61+
$label = !empty($options['label']) ? '<label class="layui-form-label' . $required_string . '">' . $options['label'] . '</label>' : '';
6162
$value = $props['value'] ?? '';
6263
$class = $props['class'] ?? 'layui-input-block';
6364

@@ -73,8 +74,8 @@ public function input($options)
7374
{
7475
[$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
7576

76-
$placeholder_string = !empty($props['placeholder']) ? ' placeholder="'.$props['placeholder'].'"' : '';
77-
$autocomplete_string = !empty($props['autocomplete']) ? ' autocomplete="'.$props['autocomplete'].'"' : '';
77+
$placeholder_string = !empty($props['placeholder']) ? ' placeholder="' . $props['placeholder'] . '"' : '';
78+
$autocomplete_string = !empty($props['autocomplete']) ? ' autocomplete="' . $props['autocomplete'] . '"' : '';
7879
$disabled_string = !empty($props['disabled']) ? ' disabled' : '';
7980
$type = $props['type'] ?? 'text';
8081

@@ -194,7 +195,7 @@ public function textArea($options)
194195
{
195196
[$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
196197

197-
$placeholder_string = !empty($props['placeholder']) ? ' placeholder="'.$props['placeholder'].'"' : '';
198+
$placeholder_string = !empty($props['placeholder']) ? ' placeholder="' . $props['placeholder'] . '"' : '';
198199
$disabled_string = !empty($props['disabled']) ? ' disabled' : '';
199200

200201
$this->htmlContent .= <<<EOF
@@ -218,7 +219,7 @@ public function richText($options)
218219
{
219220
[$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
220221

221-
$placeholder_string = !empty($props['placeholder']) ? ' placeholder="'.$props['placeholder'].'"' : '';
222+
$placeholder_string = !empty($props['placeholder']) ? ' placeholder="' . $props['placeholder'] . '"' : '';
222223
$disabled_string = !empty($props['disabled']) ? ' disabled' : '';
223224
$id = $field;
224225

@@ -264,12 +265,12 @@ public function jsonEditor($options)
264265
{
265266
[$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
266267

267-
$placeholder_string = !empty($props['placeholder']) ? ' placeholder="'.$props['placeholder'].'"' : '';
268-
$autocomplete_string = !empty($props['autocomplete']) ? ' autocomplete="'.$props['autocomplete'].'"' : '';
268+
$placeholder_string = !empty($props['placeholder']) ? ' placeholder="' . $props['placeholder'] . '"' : '';
269+
$autocomplete_string = !empty($props['autocomplete']) ? ' autocomplete="' . $props['autocomplete'] . '"' : '';
269270
$disabled_string = !empty($props['disabled']) ? ' disabled' : '';
270271
$type = $props['type'] ?? 'text';
271-
if (empty($value)){
272-
$value='{}';
272+
if (empty($value)) {
273+
$value = '{}';
273274
}
274275
$this->htmlContent .= <<<EOF
275276
@@ -368,6 +369,8 @@ public function uploadImage($options)
368369
[$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
369370
$props['acceptMime'] = $props['acceptMime'] ?? 'image/gif,image/jpeg,image/jpg,image/png';
370371
$props['url'] = $props['url'] ?? '/app/admin/upload/image';
372+
$props['multiple'] = $props['multiple'] ? 1 : 0;
373+
371374
$id = $this->createId($field);
372375

373376
unset($props['lay-verify']);
@@ -376,7 +379,8 @@ public function uploadImage($options)
376379
$props = $this->prepareProps($props);
377380
$options_string .= "\n" . $this->preparePropsToJsObject($props, 1, true);
378381

379-
$this->htmlContent .= <<<EOF
382+
if ($props['multiple'] == 0) {
383+
$this->htmlContent .= <<<EOF
380384
381385
<div class="layui-form-item">
382386
$label
@@ -393,7 +397,7 @@ public function uploadImage($options)
393397
</div>
394398
395399
EOF;
396-
$this->jsContent .= <<<EOF
400+
$this->jsContent .= <<<EOF
397401
398402
// 字段 {$options['label']} $field
399403
layui.use(["upload", "layer"], function() {
@@ -414,6 +418,7 @@ public function uploadImage($options)
414418
});
415419
layui.upload.render({
416420
elem: "#$id",$options_string
421+
url: {$props['url']},
417422
done: function (res) {
418423
if (res.code > 0) return layui.layer.msg(res.msg);
419424
this.item.prev().val(res.data.url).prev().attr("src", res.data.url);
@@ -422,6 +427,95 @@ public function uploadImage($options)
422427
});
423428
424429
EOF;
430+
} else {
431+
$this->htmlContent .= <<<EOF
432+
433+
<div class="layui-form-item">
434+
$label
435+
<div class="$class">
436+
437+
<div class="layui-upload">
438+
<input type="text" class="uploader-list" style="display:none" name="$field" value="$value" id="$id"/>
439+
<blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
440+
预览图:
441+
<div class="layui-upload-list uploader-list" style="overflow: auto;" id="uploader-list-$id">
442+
</div>
443+
</blockquote>
444+
445+
<button type="button" class="pear-btn pear-btn-primary pear-btn-sm" id="multi-upload-$id">
446+
<i class="layui-icon layui-icon-upload"></i>多图上传
447+
</button>
448+
</div>
449+
</div>
450+
</div>
451+
452+
EOF;
453+
$this->jsContent .= <<<EOF
454+
455+
// 字段 {$options['label']} $field
456+
layui.use(["upload", "layer"], function() {
457+
var upload = layui.upload;
458+
var $ = layui.$;
459+
let multiple_images = layui.$("#$id").attr("value").split(",");
460+
upload.render({
461+
elem: '#multi-upload-$id',
462+
url: {$props['url']},
463+
multiple: true,
464+
before: function(obj){
465+
layer.msg('图片上传中...', {
466+
icon: 16,
467+
shade: 0.01,
468+
time: 0
469+
})
470+
},
471+
done: function(res){
472+
layer.close(layer.msg());//关闭上传提示窗口
473+
//上传完毕
474+
$('#uploader-list-$id').append(
475+
'<div class="file-iteme">' +
476+
'<div class="handle"><i class="layui-icon layui-icon-delete"></i></div>' +
477+
'<img src='+ res.data.url +' alt="'+ res.data.name +'" >' +
478+
'</div>'
479+
);
480+
481+
//追加图片成功追加文件名至图片容器
482+
multiple_images.push(res.data.url);
483+
$('#$id').val(multiple_images);
484+
}
485+
});
486+
487+
//鼠标悬浮事件
488+
$(document).on("mouseenter mouseleave", ".file-iteme", function(event){
489+
if(event.type === "mouseenter"){
490+
//鼠标悬浮
491+
$(this).children(".info").fadeIn("fast");
492+
$(this).children(".handle").fadeIn("fast");
493+
}else if(event.type === "mouseleave") {
494+
//鼠标离开
495+
$(this).children(".info").hide();
496+
$(this).children(".handle").hide();
497+
}
498+
});
499+
500+
// 删除图片
501+
$(document).on("click", ".file-iteme .handle", function(event){
502+
var delImg = $(this).parent().children("img").attr("src")
503+
var index = multiple_images.indexOf(delImg);
504+
if (index !== -1) {
505+
multiple_images.splice(index, 1);
506+
}
507+
//重新赋值
508+
$('#$id').val(multiple_images);
509+
//删除标签
510+
$(this).parent().remove();
511+
});
512+
//多图上传 end
513+
514+
});
515+
516+
EOF;
517+
}
518+
425519

426520
}
427521

@@ -444,7 +538,7 @@ public function dateTimePicker($options)
444538
public function datePicker($options)
445539
{
446540
[$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
447-
$value_string = $value ? ' value="'.$value.'"' : '';
541+
$value_string = $value ? ' value="' . $value . '"' : '';
448542
$options_string = '';
449543
unset($props['required'], $props['lay-verify'], $props['value']);
450544
$props = $this->prepareProps($props);
@@ -463,7 +557,7 @@ public function datePicker($options)
463557
EOF;
464558
$this->jsContent .= <<<EOF
465559
466-
// 字段 {$options["label"]} $field
560+
// 字段 {$options['label']} $field
467561
layui.use(["laydate"], function() {
468562
layui.laydate.render({
469563
elem: "#$id",$options_string
@@ -548,7 +642,7 @@ public function iconPicker($options)
548642
{
549643
[$label, $field, $value, $props, $verify_string, $required_string, $class] = $this->options($options);
550644

551-
$value_string = $value ? ' value="'.$value.'"' : '';
645+
$value_string = $value ? ' value="' . $value . '"' : '';
552646
$id = $this->createId($field);
553647
$options_string = '';
554648
$props = $this->prepareProps($props);
@@ -647,7 +741,7 @@ public function selectMulti($options)
647741
{
648742
$options['props']['toolbar'] = array_merge_recursive([
649743
'show' => true,
650-
'list' => [ 'ALL', 'CLEAR', 'REVERSE' ]
744+
'list' => ['ALL', 'CLEAR', 'REVERSE']
651745
], $options['props']['toolbar'] ?? []);
652746
$this->apiSelect($options);
653747
}
@@ -686,7 +780,7 @@ public function treeSelectMulti($options)
686780
'$expandedKeys' => '$initValue'], $options['props']['tree'] ?? []);
687781
$options['props']['toolbar'] = array_merge_recursive([
688782
'$show' => true,
689-
'$list' => [ 'ALL', 'CLEAR', 'REVERSE' ]
783+
'$list' => ['ALL', 'CLEAR', 'REVERSE']
690784
], $options['props']['toolbar'] ?? []);
691785
$this->apiSelect($options);
692786
}
@@ -712,11 +806,11 @@ public function apiSelect($options)
712806
if (is_array($item)) {
713807
$item = json_encode($item, JSON_UNESCAPED_UNICODE);
714808
$item = preg_replace('/"\$([^"]+)"/', '$1', $item);
715-
$options_string .= "\n".($url?' ':' ')."$key: $item,";
809+
$options_string .= "\n" . ($url ? ' ' : ' ') . "$key: $item,";
716810
} else if (is_string($item)) {
717-
$options_string .= "\n".($url?' ':' ')."$key: \"$item\",";
811+
$options_string .= "\n" . ($url ? ' ' : ' ') . "$key: \"$item\",";
718812
} else {
719-
$options_string .= "\n".($url?' ':' ')."$key: ".var_export($item, true).",";
813+
$options_string .= "\n" . ($url ? ' ' : ' ') . "$key: " . var_export($item, true) . ',';
720814
}
721815
}
722816

@@ -824,7 +918,7 @@ public static function buildForm($table, string $type = 'insert'): Layui
824918
if ($filter == 'form_show' && !$columns[$key]['nullable'] && $default === null && ($field !== 'password' || $type === 'insert')) {
825919
if (!isset($props['lay-verify'])) {
826920
$props['lay-verify'] = 'required';
827-
// 非类似字符串类型不允许传空
921+
// 非类似字符串类型不允许传空
828922
} elseif (!in_array($columns[$key]['type'], ['string', 'text', 'mediumText', 'longText', 'char', 'binary', 'json'])
829923
&& strpos($props['lay-verify'], 'required') === false) {
830924
$props['lay-verify'] = 'required|' . $props['lay-verify'];
@@ -938,12 +1032,27 @@ public static function buildTable($table, int $indent = 0)
9381032
EOF;
9391033
break;
9401034
case 'uploadimage':
941-
$templet = <<<EOF
1035+
$props = Util::getControlProps($info['control'], $info['control_args']);
1036+
$multiple = $props['multiple'] ?? 0;
1037+
if ($multiple == 0) {
1038+
$templet = <<<EOF
9421039
9431040
templet: function (d) {
9441041
return '<img src="'+encodeURI(d['$field'])+'" style="max-width:32px;max-height:32px;" alt="" />'
9451042
}
9461043
EOF;
1044+
} else {
1045+
$templet = <<<EOF
1046+
templet: function (d) {
1047+
const images = d['$field'].split(',');
1048+
let html = '';
1049+
for (let img of images) {
1050+
html += '<img src="' + encodeURI(img.trim()) + '" style="max-width:32px;max-height:32px;" alt="" />';
1051+
}
1052+
return html;
1053+
}
1054+
EOF;
1055+
}
9471056
break;
9481057

9491058
}
@@ -961,7 +1070,7 @@ public static function buildTable($table, int $indent = 0)
9611070
$options[$option['value']] = $option['name'];
9621071
}
9631072
}
964-
$api_result .= "\napiResults[\"$field\"] = " . json_encode($options, JSON_UNESCAPED_UNICODE) . ";";
1073+
$api_result .= "\napiResults[\"$field\"] = " . json_encode($options, JSON_UNESCAPED_UNICODE) . ';';
9651074
} else {
9661075
$api_result .= "\napiResults[\"$field\"] = [];";
9671076
}
@@ -1122,12 +1231,12 @@ function travel(items) {
11221231
*/
11231232
private function prepareProps($props)
11241233
{
1125-
$raw_list = ['true','false','null','undefined'];
1234+
$raw_list = ['true', 'false', 'null', 'undefined'];
11261235
foreach ($props as $k => $v) {
11271236
if (is_array($v)) {
11281237
$props[$k] = $this->prepareProps($v);
11291238
} elseif (!in_array($v, $raw_list) && !is_numeric($v)) {
1130-
if (strpos($v, "#") === 0){
1239+
if (strpos($v, '#') === 0) {
11311240
$props[$k] = substr($v, 1);
11321241
} else {
11331242
$props[$k] = "\"$v\"";
@@ -1145,7 +1254,7 @@ private function preparePropsToJsObject($props, $indent = 0, $sub = false)
11451254
$string .= "$indent_string{\n";
11461255
}
11471256
foreach ($props as $k => $v) {
1148-
if (!preg_match("#^[a-zA-Z0-9_]+$#", $k)) {
1257+
if (!preg_match('#^[a-zA-Z0-9_]+$#', $k)) {
11491258
$k = "'$k'";
11501259
}
11511260
if (is_array($v)) {
@@ -1157,7 +1266,7 @@ private function preparePropsToJsObject($props, $indent = 0, $sub = false)
11571266
if (!$sub) {
11581267
$string .= "$indent_string}\n";
11591268
}
1160-
return trim($string,"\n");
1269+
return trim($string, "\n");
11611270
}
11621271

11631272

0 commit comments

Comments
 (0)