提交 4a280bf4 编写于 作者: E eddy8

Merge branch 'master' into laravel-6.x

......@@ -17,6 +17,9 @@ use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\View\View;
use App\Model\Admin\Tag;
use App\Model\Admin\ContentTag;
use Illuminate\Support\Facades\DB;
class ContentController extends Controller
{
......@@ -117,15 +120,36 @@ class ContentController extends Controller
}
try {
ContentRepository::add($request->only(
EntityFieldRepository::getFields($entity)
DB::beginTransaction();
$content = ContentRepository::add($request->only(
EntityFieldRepository::getSaveFields($entity)
));
// 标签类型字段另外处理 多对多关联
$inputTagsField = EntityFieldRepository::getInputTagsField($entity);
$tags = null;
if ($inputTagsField) {
$tags = $request->post($inputTagsField->name);
}
if (!is_null($tags) && $tags = json_decode($tags, true)) {
foreach ($tags as $v) {
$tag = Tag::firstOrCreate(['name' => $v['value']]);
ContentTag::firstOrCreate(
['entity_id' => $entity, 'content_id' => $content->id, 'tag_id' => $tag->id]
);
}
}
DB::commit();
return [
'code' => 0,
'msg' => '新增成功',
'redirect' => route('admin::content.index', ['entity' => $entity])
];
} catch (QueryException $e) {
DB::rollBack();
\Log::error($e);
return [
'code' => 1,
......@@ -174,26 +198,37 @@ class ContentController extends Controller
return $result;
}
$fieldInfo = EntityFieldRepository::getByEntityId($entity)
->where('is_edit', EntityField::EDIT_ENABLE)
->pluck('form_type', 'name')
->toArray();
$data = [];
foreach ($fieldInfo as $k => $v) {
if ($v === 'checkbox') {
$data[$k] = '';
}
}
$data = array_merge($data, $request->only(array_keys($fieldInfo)));
$data = $this->getUpdateData($request, $entity);
try {
DB::beginTransaction();
ContentRepository::update($id, $data);
// 标签类型字段另外处理 多对多关联
$inputTagsField = EntityFieldRepository::getInputTagsField($entity);
$tags = null;
if ($inputTagsField && $inputTagsField->is_edit === EntityField::EDIT_ENABLE) {
$tags = $request->post($inputTagsField->name);
}
if (!is_null($tags) && $tags = json_decode($tags, true)) {
$tagIds = [];
foreach ($tags as $v) {
$tag = Tag::firstOrCreate(['name' => $v['value']]);
ContentTag::firstOrCreate(['entity_id' => $entity, 'content_id' => $id, 'tag_id' => $tag->id]);
$tagIds[] = $tag->id;
}
if ($tagIds) {
ContentTag::where('entity_id', $entity)->where('content_id', $id)->whereNotIn('tag_id', $tagIds)->delete();
}
}
DB::commit();
return [
'code' => 0,
'msg' => '编辑成功',
'redirect' => route('admin::content.index', ['entity' => $entity])
];
} catch (QueryException $e) {
DB::rollBack();
\Log::error($e);
return [
'code' => 1,
......@@ -336,4 +371,16 @@ class ContentController extends Controller
return $view;
}
protected function getUpdateData($request, $entity)
{
$fieldInfo = EntityFieldRepository::getUpdateFields($entity);
$data = [];
foreach ($fieldInfo as $k => $v) {
if ($v === 'checkbox') {
$data[$k] = '';
}
}
return array_merge($data, $request->only(array_keys($fieldInfo)));
}
}
......@@ -73,7 +73,7 @@ class EntityController extends Controller
public function save(EntityRequest $request)
{
try {
$createDB = $request->post('is_modify_db', false);
$createDB = $request->post('is_modify_db');
EntityRepository::add($request->only($this->formNames), $createDB);
return [
'code' => 0,
......
......@@ -108,8 +108,19 @@ class EntityFieldController extends Controller
'msg' => '新增失败:无效字段类型',
];
}
// 一个模型只能有一个 inputTags 表单类型
if (EntityFieldRepository::getInputTagsField($data['entity_id'])) {
return [
'code' => 4,
'msg' => '新增失败:一个模型只能有一个标签输入框表单类型',
];
}
$modifyDB = $request->post('is_modify_db', false);
$modifyDB = $request->post('is_modify_db');
// inputTags类型表单不需要添加数据库字段
if (in_array($data['type'], ['inputTags'], true)) {
$modifyDB = false;
}
if ($modifyDB) {
Schema::table($table->table_name, function (Blueprint $table) use ($data) {
$m = $data['type'];
......@@ -191,6 +202,13 @@ class EntityFieldController extends Controller
$data['is_edit'] = $data['is_edit'] ?? EntityField::EDIT_DISABLE;
$data['is_required'] = $data['is_required'] ?? EntityField::REQUIRED_DISABLE;
$data['is_show_inline'] = $data['is_show_inline'] ?? EntityField::SHOW_NOT_INLINE;
// 一个模型只能有一个 inputTags 表单类型
if (EntityFieldRepository::getInputTagsField($data['entity_id'])) {
return [
'code' => 4,
'msg' => '编辑失败:一个模型只能有一个标签输入框表单类型',
];
}
try {
unset($data['field_length'], $data['field_total'], $data['field_scale'], $data['entity_id']);
EntityFieldRepository::update($id, $data);
......@@ -243,6 +261,12 @@ class EntityFieldController extends Controller
}
}
/**
* 模型字段管理-字段快捷更新接口
*
* @param Request $request
* @param int $id
*/
public function listUpdate(Request $request, $id)
{
try {
......
<?php
/**
* @author Eddy <cumtsjh@163.com>
*/
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\TagRequest;
use App\Repository\Admin\TagRepository;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\View\View;
use App\Model\Admin\ContentTag;
use Illuminate\Support\Facades\DB;
class TagController extends Controller
{
protected $formNames = ['name', 'created_at'];
public function __construct()
{
parent::__construct();
$this->breadcrumb[] = ['title' => '标签列表', 'url' => route('admin::tag.index')];
}
/**
* 标签管理-标签列表
*
*/
public function index()
{
$this->breadcrumb[] = ['title' => '标签列表', 'url' => ''];
return view('admin.tag.index', ['breadcrumb' => $this->breadcrumb]);
}
/**
* 标签列表数据接口
*
* @param Request $request
* @return array
*/
public function list(Request $request)
{
$perPage = (int) $request->get('limit', 50);
$condition = $request->only($this->formNames);
$data = TagRepository::list($perPage, $condition);
return $data;
}
/**
* 标签管理-新增标签
*
*/
public function create()
{
$this->breadcrumb[] = ['title' => '新增标签', 'url' => ''];
return view('admin.tag.add', ['breadcrumb' => $this->breadcrumb]);
}
/**
* 标签管理-保存标签
*
* @param TagRequest $request
* @return array
*/
public function save(TagRequest $request)
{
try {
TagRepository::add($request->only($this->formNames));
return [
'code' => 0,
'msg' => '新增成功',
'redirect' => true
];
} catch (QueryException $e) {
return [
'code' => 1,
'msg' => '新增失败:' . (Str::contains($e->getMessage(), 'Duplicate entry') ? '当前标签已存在' : '其它错误'),
'redirect' => false
];
}
}
/**
* 标签管理-编辑标签
*
* @param int $id
* @return View
*/
public function edit($id)
{
$this->breadcrumb[] = ['title' => '编辑标签', 'url' => ''];
$model = TagRepository::find($id);
return view('admin.tag.add', ['id' => $id, 'model' => $model, 'breadcrumb' => $this->breadcrumb]);
}
/**
* 标签管理-更新标签
*
* @param TagRequest $request
* @param int $id
* @return array
*/
public function update(TagRequest $request, $id)
{
$data = $request->only($this->formNames);
try {
TagRepository::update($id, $data);
return [
'code' => 0,
'msg' => '编辑成功',
'redirect' => true
];
} catch (QueryException $e) {
return [
'code' => 1,
'msg' => '编辑失败:' . (Str::contains($e->getMessage(), 'Duplicate entry') ? '当前标签已存在' : '其它错误'),
'redirect' => false
];
}
}
/**
* 标签管理-删除标签
*
* @param int $id
*/
public function delete($id)
{
try {
DB::beginTransaction();
TagRepository::delete($id);
ContentTag::where('tag_id', $id)->delete();
DB::commit();
return [
'code' => 0,
'msg' => '删除成功',
'redirect' => route('admin::tag.index')
];
} catch (\RuntimeException $e) {
DB::rollBack();
return [
'code' => 1,
'msg' => '删除失败:' . $e->getMessage(),
'redirect' => false
];
}
}
}
......@@ -19,10 +19,9 @@ class UEditorController extends NEditorController
* ueditor-ueditor后端服务
*
* @param Request $request
* @param null $type
* @return array|mixed
*/
public function serve(Request $request, $type = null)
public function serve(Request $request)
{
$action = $request->input('action');
if (!method_exists(\self::class, $action)) {
......
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
use App\Model\Admin\Tag;
use Illuminate\Validation\Rule;
class TagRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|max:20',
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
return [
'name.required' => '名称不能为空',
'name.max' => '名称长度不能大于20',
];
}
}
<?php
/**
* @author Eddy <cumtsjh@163.com>
*/
namespace App\Model\Admin;
class ContentTag extends Model
{
protected $guarded = [];
}
<?php
/**
* @author Eddy <cumtsjh@163.com>
*/
namespace App\Model\Admin;
class Tag extends Model
{
protected $guarded = [];
public static $searchField = [
'name' => '名称'
];
public static $listField = [
'name' => '名称'
];
}
......@@ -6,8 +6,13 @@
namespace App\Repository\Admin;
use App\Model\Admin\Content;
use App\Model\Admin\ContentTag;
use App\Repository\Searchable;
/**
* 使用当前类时必须先调用 setTable 方法设置所要操作的数据库表
* @package App\Repository\Admin
*/
class ContentRepository
{
use Searchable;
......@@ -43,7 +48,8 @@ class ContentRepository
public static function add($data)
{
return self::$model->setRawAttributes(self::processParams($data))->save();
self::$model->setRawAttributes(self::processParams($data))->save();
return self::$model;
}
public static function update($id, $data)
......@@ -102,4 +108,16 @@ class ContentRepository
->orderBy('id', 'desc')
->paginate($perPage);
}
public static function tags($entityId, $contentId)
{
return ContentTag::query()->where('entity_id', $entityId)->where('content_id', $contentId)
->leftJoin('tags', 'tags.id', '=', 'content_tags.tag_id')
->get(['name', 'tag_id']);
}
public static function tagNames($entityId, $contentId)
{
return self::tags($entityId, $contentId)->implode('name', ',');
}
}
......@@ -70,4 +70,25 @@ class EntityFieldRepository
return EntityField::query()->select('name')->where('entity_id', $entityId)->get()
->pluck('name')->toArray();
}
public static function getSaveFields($entityId)
{
return EntityField::query()->select('name')->where('entity_id', $entityId)
->whereNotIn('form_type', ['inputTags'])->get()->pluck('name')->toArray();
}
public static function getUpdateFields($entityId)
{
return EntityField::query()->select('name')->where('entity_id', $entityId)
->where('is_edit', EntityField::EDIT_ENABLE)
->whereNotIn('form_type', ['inputTags'])
->get()->pluck('form_type', 'name')->toArray();
}
public static function getInputTagsField($entityId)
{
return EntityField::query()->where('entity_id', $entityId)
->where('form_type', 'inputTags')
->first();
}
}
<?php
/**
* @author Eddy <cumtsjh@163.com>
*/
namespace App\Repository\Admin;
use App\Model\Admin\Tag;
use App\Repository\Searchable;
class TagRepository
{
use Searchable;
public static function list($perPage, $condition = [])
{
$data = Tag::query()
->where(function ($query) use ($condition) {
Searchable::buildQuery($query, $condition);
})
->orderBy('id', 'desc')
->paginate($perPage);
$data->transform(function ($item) {
$item->editUrl = route('admin::tag.edit', ['id' => $item->id]);
$item->deleteUrl = route('admin::tag.delete', ['id' => $item->id]);
return $item;
});
return [
'code' => 0,
'msg' => '',
'count' => $data->total(),
'data' => $data->items(),
];
}
public static function add($data)
{
return Tag::query()->create($data);
}
public static function update($id, $data)
{
return Tag::query()->where('id', $id)->update($data);
}
public static function find($id)
{
return Tag::query()->find($id);
}
public static function delete($id)
{
return Tag::destroy($id);
}
}
......@@ -74,6 +74,7 @@ return [
'checkbox' => '复选框',
'select' => '下拉选择',
'selectMulti' => '下拉选择(多选)',
'inputTags' => '标签输入框',
'upload' => '图片上传(单图)',
'uploadMulti' => '图片上传(多图)',
'datetime' => '日期时间',
......
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTagsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 20)->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tags');
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateContentTagsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('content_tags', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('entity_id')->unsigned();
$table->integer('content_id')->unsigned();
$table->bigInteger('tag_id')->unsigned();
$table->timestamps();
$table->index('entity_id', 'content_id');
$table->index('entity_id', 'tag_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('content_tags');
}
}
......@@ -94,6 +94,21 @@ INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `order`, `route`, `url`, `gr
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `is_lock_name`, `created_at`, `updated_at`) VALUES ('121', '新增会员', '26', '1', '100', 'admin::user.create', '/admin/users/create', '会员管理', 'admin', '', '0', '2019-04-04 09:04:35', '2019-04-04 09:06:41');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `is_lock_name`, `created_at`, `updated_at`) VALUES ('120', '会员列表数据接口', '26', '0', '100', 'admin::user.list', '/admin/users/list', '会员管理', 'admin', '', '0', '2019-04-04 09:04:35', '2019-04-08 14:07:28');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `is_lock_name`, `created_at`, `updated_at`) VALUES ('119', '会员列表', '26', '1', '100', 'admin::user.index', '/admin/users', '会员管理', 'admin', '', '0', '2019-04-04 09:04:35', '2019-04-04 09:06:41');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('140', '删除标签', '26', '0', '0', '30', 'admin::tag.delete', '', '标签管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 10:54:23');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('139', '更新标签', '26', '0', '0', '30', 'admin::tag.update', '', '标签管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 10:54:23');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('138', '编辑标签', '26', '0', '0', '30', 'admin::tag.edit', '', '标签管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 10:54:23');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('137', '保存标签', '26', '0', '0', '30', 'admin::tag.save', '/admin/tags', '标签管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 10:54:23');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('136', '新增标签', '26', '1', '0', '30', 'admin::tag.create', '/admin/tags/create', '标签管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 10:54:23');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('135', '标签列表数据接口', '26', '0', '0', '30', 'admin::tag.list', '/admin/tags/list', '', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 10:54:23');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('134', '标签列表', '26', '1', '0', '29', 'admin::tag.index', '/admin/tags', '标签管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 10:54:35');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('133', '删除评论', '26', '0', '0', '90', 'admin::comment.delete', '', '评论管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 11:03:11');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('132', '更新评论', '26', '0', '0', '90', 'admin::comment.update', '', '评论管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 11:03:11');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('131', '编辑评论', '26', '0', '0', '90', 'admin::comment.edit', '', '评论管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 11:03:11');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('130', '评论列表数据接口', '26', '0', '0', '90', 'admin::comment.list', '/admin/comments/list', '', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 11:03:11');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('129', '评论列表', '26', '1', '0', '90', 'admin::comment.index', '/admin/comments', '评论管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 11:03:11');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('128', '内容批量操作', '0', '0', '0', '0', 'admin::content.batch', '', '内容管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 09:49:15');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('127', '字段快捷更新接口', '0', '0', '0', '0', 'admin::entityField.listUpdate', '', '模型字段管理', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 10:43:10');
INSERT INTO `menus` (`id`, `name`, `pid`, `status`, `is_lock_name`, `order`, `route`, `url`, `group`, `guard_name`, `remark`, `created_at`, `updated_at`) VALUES ('126', 'ueditor后端服务', '0', '0', '0', '0', 'admin::ueditor.serve', '/admin/ueditor/serve', 'ueditor', 'admin', '', '2019-10-23 09:49:15', '2019-10-23 09:51:28');
EOL;
DB::unprepared($sql);
}
......
.tagify{--tags-border-color:#DDD;--tag-bg:#E5E5E5;--tag-hover:#D3E2E2;--tag-text-color:black;--tag-text-color--edit:black;--tag-pad:0.3em 0.5em;--tag-inset-shadow-size:1.1em;--tag-invalid-color:#D39494;--tag-remove-bg:rgba($tag-remove, .3);--tag-remove-btn-bg:none;--tag-remove-btn-bg--hover:#c77777;display:flex;align-items:flex-start;flex-wrap:wrap;border:1px solid #ddd;border:1px solid var(--tags-border-color);padding:0;line-height:1.1;cursor:text;position:relative;transition:.1s}@keyframes tags--bump{30%{transform:scale(1.2)}}.tagify:hover{border-color:#ccc}.tagify.tagify--focus{border-color:#85c8ea}.tagify[readonly]{cursor:default}.tagify[readonly]>.tagify__input{visibility:hidden;width:0;margin:5px 0}.tagify[readonly] .tagify__tag__removeBtn{display:none}.tagify[readonly] .tagify__tag>div{padding:.3em .5em;padding:var(--tag-pad)}.tagify[readonly] .tagify__tag>div::before{background:linear-gradient(45deg,var(--tag-bg) 25%,transparent 25%,transparent 50%,var(--tag-bg) 50%,var(--tag-bg) 75%,transparent 75%,transparent) 0/5px 5px;box-shadow:none;filter:brightness(.95)}.tagify+input,.tagify+textarea{display:none!important}.tagify__tag{display:inline-flex;align-items:center;margin:5px 0 5px 5px;position:relative;z-index:1;cursor:default;transition:.13s ease-out}.tagify__tag.tagify--editable>div{color:#000;color:var(--tag-text-color--edit)}.tagify__tag.tagify--editable>div::before{box-shadow:0 0 0 2px #d3e2e2 inset!important;box-shadow:0 0 0 2px var(--tag-hover) inset!important}.tagify__tag.tagify--editable.tagify--invalid>div::before{box-shadow:0 0 0 2px #d39494 inset!important;box-shadow:0 0 0 2px var(--tag-invalid-color) inset!important}.tagify__tag>div{vertical-align:top;box-sizing:border-box;max-width:100%;padding:.3em .5em;padding:var(--tag-pad);color:#000;color:var(--tag-text-color);line-height:inherit;border-radius:3px;-webkit-user-select:none;user-select:none;transition:.13s ease-out}.tagify__tag>div>*{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;vertical-align:top;min-width:10px}.tagify__tag>div>[contenteditable]{outline:0;-webkit-user-select:text;user-select:text;cursor:text;margin:-2px;padding:2px}.tagify__tag>div::before{content:'';position:absolute;border-radius:inherit;left:0;top:0;right:0;bottom:0;z-index:-1;pointer-events:none;transition:120ms ease;animation:tags--bump .3s ease-out 1;box-shadow:0 0 0 1.1em #e5e5e5 inset;box-shadow:0 0 0 var(--tag-inset-shadow-size) var(--tag-bg) inset}.tagify__tag:hover:not([readonly]) div::before{top:-2px;right:-2px;bottom:-2px;left:-2px;box-shadow:0 0 0 1.1em #d3e2e2 inset;box-shadow:0 0 0 var(--tag-inset-shadow-size) var(--tag-hover) inset}.tagify__tag.tagify--noAnim{animation:none}.tagify__tag.tagify--hide{width:0!important;padding-left:0;padding-right:0;margin-left:0;margin-right:0;opacity:0;transform:scale(0);transition:.3s;pointer-events:none}.tagify__tag.tagify--mark div::before{animation:none}.tagify__tag.tagify--notAllowed div>span{opacity:.5}.tagify__tag.tagify--notAllowed div::before{box-shadow:0 0 0 1.1em rgba(211,148,148,.44) inset!important;box-shadow:0 0 0 var(--tag-inset-shadow-size) var(--tag-invalid-bg) inset!important;transition:.2s}.tagify__tag[readonly] .tagify__tag__removeBtn{display:none}.tagify__tag[readonly]>div::before{background:linear-gradient(45deg,var(--tag-bg) 25%,transparent 25%,transparent 50%,var(--tag-bg) 50%,var(--tag-bg) 75%,transparent 75%,transparent) 0/5px 5px;box-shadow:none;filter:brightness(.95)}.tagify__tag__removeBtn{order:5;font:14px/16px Serif;background:0 0;background:var(--tag-remove-btn-bg);color:#000;color:var(--tag-text-color);width:14px;height:14px;text-align:center;border-radius:50px;cursor:pointer;margin-right:4.66667px;margin-left:-4.66667px;transition:.2s ease-out}.tagify__tag__removeBtn::after{content:"\00D7"}.tagify__tag__removeBtn:hover{color:#fff;background:#c77777;background:var(--tag-remove-btn-bg--hover)}.tagify__tag__removeBtn:hover+div>span{opacity:.5}.tagify__tag__removeBtn:hover+div::before{box-shadow:0 0 0 1.1em rgba(211,148,148,.3) inset!important;box-shadow:0 0 0 var(--tag-inset-shadow-size) var(--tag-remove-bg) inset!important;transition:.2s}.tagify:not(.tagify--mix) .tagify__input{white-space:nowrap}.tagify:not(.tagify--mix) .tagify__input br{display:none}.tagify:not(.tagify--mix) .tagify__input *{display:inline;white-space:nowrap}.tagify__input{display:block;min-width:110px;margin:5px;padding:.3em .5em;padding:var(--tag-pad,.3em .5em);line-height:inherit;position:relative}.tagify__input::before{display:inline-block;width:0}.tagify__input:empty::before{transition:.2s ease-out;opacity:.5;transform:none;width:auto}.tagify__input:focus{outline:0}.tagify__input:focus::before{transition:.2s ease-out;opacity:0;transform:translatex(6px)}@supports (-moz-appearance:none){.tagify__input:focus::before{display:none}}.tagify__input:focus:empty::before{transition:.2s ease-out;opacity:.3;transform:none}@supports (-moz-appearance:none){.tagify__input:focus:empty::before{display:inline-block}}.tagify__input::before{content:attr(data-placeholder);line-height:1.8;position:absolute;top:0;z-index:1;color:#000;white-space:nowrap;pointer-events:none;opacity:0}@supports (-moz-appearance:none){.tagify__input::before{line-height:inherit;position:relative}}.tagify__input::after{content:attr(data-suggest);color:#000;opacity:.3;pointer-events:none}.tagify__input .tagify__tag{margin:0}.tagify__input .tagify__tag>div{padding-top:0;padding-bottom:0}.tagify--mix .tagify__input{padding:5px;margin:0;width:100%;height:100%;line-height:1.7}.tagify--select::after{content:'>';opacity:.5;position:absolute;top:50%;right:0;bottom:0;font:16px monospace;line-height:8px;height:8px;pointer-events:none;transform:translate(-150%,-50%) scaleX(1.2) rotate(90deg);transition:.2s ease-in-out}.tagify--select[aria-expanded=true]::after{transform:translate(-150%,-50%) rotate(270deg) scaleY(1.2)}.tagify--select .tagify__tag{position:absolute;top:0;right:1.8em;bottom:0}.tagify--select .tagify__tag div{display:none}.tagify--select .tagify__input{width:100%}.tagify--invalid{--tags-border-color:#D39494}.tagify__dropdown{position:absolute;z-index:9999;background:#fff;max-height:300px;overflow:auto;border:1px solid #85c8ea;box-shadow:0 2px 4px -2px rgba(0,0,0,.2);box-sizing:border-box}.tagify__dropdown__item{box-sizing:inherit;padding:.35em .6em;margin:2px;cursor:pointer;border-radius:3px;position:relative;outline:0}.tagify__dropdown__item--active{background:#e5e5e5}.tagify__dropdown__item:active{filter:brightness(105%)}
\ No newline at end of file
此差异已折叠。
......@@ -8,7 +8,7 @@
`LightCMS&Laravel`学习交流QQ群:**972796921**
**注意:如果想使用基于最新`Laravel 6.0`的版本,可使用`laravel-6.0`分支。**
**注意:如果想使用基于最新`Laravel 6.0(6.x)`的版本,可使用`laravel-6.x`分支。**
## 功能点一览
后台:
......@@ -91,7 +91,7 @@ server {
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#不同配置对应不同的环境配置文件。比如此处应用会加载.env.pro文件,默认不配置会加载.env文件。此处可根据项目需要自行配制。
#fastcgi_param APP_ENV pro;
#fastcgi_param APP_ENV pro;
include fastcgi_params;
}
}
......@@ -133,6 +133,11 @@ public function index()
$siteName = config('light_config.SITE_NAME');
```
## 标签管理
模型内容**打标签**是站点的一项常用功能,`lightCMS`内置了打标签功能。添加模型字段时选择表单类型为`标签输入框`即可。
`lightCMS`采用中间表(content_tags)来实现标签和模型内容的多对多关联关系。
## 模型管理
`lightCMS`支持在后台直接创建模型,并可对模型的表字段进行自定义设置。设置完模型字段后,就不需要做其它工作了,模型的增删改查功能系统已经内置。
......
......@@ -427,6 +427,58 @@
formSelects.render('select-{{ $field->name }}');
</script>
@break
@case('inputTags')
@if(!isset($tagify_init))
@php
// https://github.com/yairEO/tagify
$tagify_init = true
@endphp
<link rel="stylesheet" type="text/css" href="/public/vendor/tagify/tagify.css"/>
<script type="text/javascript" src="/public/vendor/tagify/tagify.min.js"></script>
@endif
<div class="layui-form-item">
<label class="layui-form-label">{{ $field->form_name }}</label>
<div class="layui-input-block" style="">
<input name="{{ $field->name }}" placeholder="输入标签,按回车键或TAB键可新增标签" value="@if(isset($model)) {{ \App\Repository\Admin\ContentRepository::tagNames($entity, $model->id) }} @endif">
</div>
</div>
<script>
var input = document.querySelector('input[name={{ $field->name }}]'),
// init Tagify script on the above inputs
tagify = new Tagify(input, {
dropdown: {
enabled: 1,
maxItems: 50,
highlightFirst: true
}
});
// Chainable event listeners
tagify.on('input', onInput);
// on character(s) added/removed (user is typing/deleting)
function onInput(e){
var value = e.detail.value;
tagify.settings.whitelist = [];
tagify.dropdown.hide.call(tagify);
$.ajax({
url: "{{ route('admin::tag.list') }}" + "?page=1&limit=50" + "&name=" + value,
method: "GET",
dataType: "json",
success: function (d) {
if (d.code === 0 && d.count > 0) {
var data = [];
for (var j = 0; j < d.data.length; j++) {
data.push(d.data[j].name);
}
tagify.settings.whitelist = data;
tagify.dropdown.show.call(tagify, value);
}
}
});
}
</script>
@break
@endswitch
@endforeach
......
@extends('admin.base')
@section('content')
<div class="layui-card">
@include('admin.breadcrumb')
<div class="layui-card-body">
<form class="layui-form" action="@if(isset($id)){{ route('admin::tag.update', ['id' => $id]) }}@else{{ route('admin::tag.save') }}@endif" method="post">
@if(isset($id)) {{ method_field('PUT') }} @endif
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input type="text" name="name" required lay-verify="required" autocomplete="off" class="layui-input" value="{{ $model->name ?? '' }}">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formAdminUser" id="submitBtn">提交</button>
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
</div>
</div>
</form>
</div>
</div>
@endsection
@section('js')
<script>
var form = layui.form;
//监听提交
form.on('submit(formAdminUser)', function(data){
window.form_submit = $('#submitBtn');
form_submit.prop('disabled', true);
$.ajax({
url: data.form.action,
data: data.field,
success: function (result) {
if (result.code !== 0) {
form_submit.prop('disabled', false);
layer.msg(result.msg, {shift: 6});
return false;
}
layer.msg(result.msg, {icon: 1}, function () {
if (result.reload) {
location.reload();
}
if (result.redirect) {
location.href = '{!! url()->previous() !!}';
}
});
}
});
return false;
});
</script>
@endsection
@extends('admin.base')
@section('content')
@include('admin.breadcrumb')
<div class="layui-card">
<div class="layui-form layui-card-header light-search" style="height: auto">
<form>
<input type="hidden" name="action" value="search">
@include('admin.searchField', ['data' => App\Model\Admin\Tag::$searchField])
<div class="layui-inline">
<label class="layui-form-label">创建日期</label>
<div class="layui-input-inline">
<input type="text" name="created_at" class="layui-input" id="created_at" value="{{ request()->get('created_at') }}">
</div>
</div>
<div class="layui-inline">
<button class="layui-btn layuiadmin-btn-list" lay-filter="form-search" id="submitBtn">
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
</button>
</div>
</form>
</div>
<div class="layui-card-body">
<div class="layui-row">
<div class="layui-col-xs3">
<div style="padding: 10px 0">
<div style="margin-bottom: 10px">
<span class="layui-badge layui-bg-blue">新增标签</span>
</div>
<div style="margin-right: 10px">
<form class="layui-form" action="{{ route('admin::tag.save') }}" method="post">
<div class="layui-form-item">
<label>名称</label>
<input type="text" name="name" required lay-verify="required" autocomplete="off" class="layui-input" value="">
</div>
<div class="layui-form-item">
<button class="layui-btn" lay-submit lay-filter="formAddTag" id="submitBtn">新增标签</button>
</div>
</form>
</div>
</div>
</div>
<div class="layui-col-xs9">
<table class="layui-table" lay-data="{url:'{{ route('admin::tag.list') }}?{{ request()->getQueryString() }}', page:true, limit:50, id:'test', toolbar:'<div><a href=\'{{ route('admin::tag.create') }}\'><i class=\'layui-icon layui-icon-add-1\'></i>新增标签</a></div>'}" lay-filter="test">
<thead>
<tr>
<th lay-data="{field:'id', width:80, sort: true}">ID</th>
@include('admin.listHead', ['data' => App\Model\Admin\Tag::$listField])
<th lay-data="{field:'created_at'}">添加时间</th>
<th lay-data="{field:'updated_at'}">更新时间</th>
<th lay-data="{width:200, templet:'#action'}">操作</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
@endsection
<script type="text/html" id="action">
<a href="<% d.editUrl %>" class="layui-table-link" title="编辑"><i class="layui-icon layui-icon-edit"></i></a>
<a href="javascript:;" class="layui-table-link" title="删除" style="margin-left: 10px" onclick="deleteMenu('<% d.deleteUrl %>')"><i class="layui-icon layui-icon-delete"></i></a>
</script>
@section('js')
<script>
var laytpl = layui.laytpl;
laytpl.config({
open: '<%',
close: '%>'
});
var laydate = layui.laydate;
laydate.render({
elem: '#created_at',
range: '~'
});
function deleteMenu (url) {
layer.confirm('确定删除?', function(index){
$.ajax({
url: url,
data: {'_method': 'DELETE'},
success: function (result) {
if (result.code !== 0) {
layer.msg(result.msg, {shift: 6});
return false;
}
layer.msg(result.msg, {icon: 1}, function () {
if (result.reload) {
location.reload();
}
if (result.redirect) {
location.href = '{!! url()->previous() !!}';
}
});
}
});
layer.close(index);
});
}
var form = layui.form;
//监听提交
form.on('submit(formAddTag)', function(data){
window.form_submit = $('#submitBtn');
form_submit.prop('disabled', true);
$.ajax({
url: data.form.action,
data: data.field,
success: function (result) {
if (result.code !== 0) {
form_submit.prop('disabled', false);
layer.msg(result.msg, {shift: 6});
return false;
}
layer.msg(result.msg, {icon: 1}, function () {
location.reload();
});
}
});
return false;
});
</script>
@endsection
<?php
Route::get('/tags', 'TagController@index')->name('tag.index');
Route::get('/tags/list', 'TagController@list')->name('tag.list');
Route::get('/tags/create', 'TagController@create')->name('tag.create');
Route::post('/tags', 'TagController@save')->name('tag.save');
Route::get('/tags/{id}/edit', 'TagController@edit')->name('tag.edit');
Route::put('/tags/{id}', 'TagController@update')->name('tag.update');
Route::delete('/tags/{id}', 'TagController@delete')->name('tag.delete');
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册