This commit is contained in:
2024-11-21 16:40:44 +08:00
commit a5029a2be7
69 changed files with 3160 additions and 0 deletions

View File

@ -0,0 +1,41 @@
{
"type": "think-admin-plugin",
"name": "jerryyan/think-plugs-cms",
"license": "WTFPL",
"homepage": "https://thinkadmin.top",
"description": "CMS Plugin for ThinkAdmin",
"authors": [
{
"name": "Jerry Yan",
"email": "jerryyan0912@qq.com"
}
],
"require": {
"php": ">7.1"
},
"autoload": {
"psr-4": {
"plugin\\cms\\": "src"
}
},
"extra": {
"config": {
"type": "module",
"name": "新闻资讯",
"document": "https://thinkadmin.top/plugin/think-plugs-wechat.html",
"description": "新闻资讯"
},
"think": {
"services": [
"plugin\\cms\\Service"
]
}
},
"minimum-stability": "dev",
"config": {
"sort-packages": true,
"allow-plugins": {
"zoujingli/think-install": true
}
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace plugin\cms;
use think\admin\Plugin;
use think\facade\View;
class Service extends Plugin
{
protected $appName = '文章管理';
protected $appCode = 'cms';
protected $package = 'jerryyan/think-plugs-cms';
public static function register(): void
{
View::assign("_YES_OR_NO", [
'1' => '是',
'0' => '否'
]);
}
public static function menu(): array
{
$code = app(static::class)->appCode;
return [
[
'name' => '文章管理',
'subs' => [
['name' => '文章分类', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/category/index"],
['name' => '文章列表', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/article/index"],
['name' => '轮播图列表', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/banner/index"],
]
]
];
}
}

View File

@ -0,0 +1,143 @@
<?php
namespace plugin\cms\controller;
use plugin\cms\model\CmsArticle;
use plugin\cms\model\CmsCateArticle;
use plugin\cms\model\CmsCategory;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
/**
* 文章管理
*/
class Article extends Controller
{
/**
* 文章管理
* @auth true
* @menu true
* @login true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->title = '文章管理';
CmsArticle::mQuery()->layTable(function () {
}, static function (QueryHelper $query) {
$query->where('is_deleted', 0);
$query->with(['category']);
$query->equal('status')->like('title');
$query->timeBetween('create_at');
});
}
public function _index_page_filter(&$list)
{
foreach ($list as &$item) {
$cate_list = $item['category'];
$item['cate_name'] = join(',', array_column($cate_list, 'name'));
}
}
/**
* 添加文章
* @auth true
* @menu true
* @login true
* @return void
*/
public function add()
{
$this->title = '添加文章';
$this->cates = CmsCategory::getList();
$this->cate_ids = [];
CmsArticle::mForm('form');
}
/**
* 编辑文章
* @auth true
* @menu true
* @login true
* @return void
*/
public function edit()
{
$this->title = '编辑文章';
$this->cates = CmsCategory::getList();
$this->id = $this->request->param('id');
$this->cate_ids = CmsCateArticle::where(['aid' => $this->id])->column('cid');
CmsArticle::mForm('form', "id");
}
public function _form_filter(&$data)
{
unset($data['cate_ids']);
}
public function _form_result($state, $data)
{
if (isset($data['id'])) {
CmsCateArticle::where(['aid' => $data['id']])->delete();
$cate_ids = $this->request->param('cate_ids', []);
foreach ($cate_ids as $cid) {
CmsCateArticle::create(['aid' => $data['id'], 'cid' => $cid]);
}
}
}
/**
* 删除文章
* @auth true
* @menu true
* @login true
* @return void
*/
public function remove()
{
CmsArticle::mDelete('id');
}
/**
* 修改文章状态
* @auth true
* @menu true
* @login true
* @return void
*/
public function status()
{
CmsArticle::mSave($this->_vali([
'id.require' => '文章ID不能为空',
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]), 'id');
}
public function _status_save_result($state, $data)
{
if ($state) {
$status = $this->request->param('status');
$id = $this->request->param('id');
if ($status == 1) {
CmsArticle::mk()->where('id', $id)->save([
'publish_at' => date('Y-m-d H:i:s')
]);
}
}
}
public function sort()
{
CmsArticle::mSave($this->_vali([
'id.require' => '文章ID不能为空',
'sort.require' => '排序值不能为空!',
'sort.number' => '排序必须为数字!',
'sort.between:0,9999' => '排序值必须为0~9999之间!',
]), 'id');
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace plugin\cms\controller;
use plugin\cms\model\CmsBanner;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
class Banner extends Controller
{
/**
* 轮播图管理
* @auth true
* @menu true
* @login true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->title = '轮播图管理';
CmsBanner::mQuery()->layTable(function () {
}, static function (QueryHelper $query) {
$query->equal('status')->like('title');
$query->timeBetween('create_at');
});
}
/**
* 添加轮播图
* @auth true
* @menu true
* @login true
* @return void
*/
public function add()
{
$this->title = '添加栏目';
$this->_applyFormToken();
CmsBanner::mForm('form');
}
/**
* 编辑轮播图
* @auth true
* @menu true
* @login true
* @return void
*/
public function edit()
{
$this->title = '编辑栏目';
$this->id = $this->request->param('id', 0, 'intval'); //当前栏目ID
$this->_applyFormToken();
CmsBanner::mForm('form');
}
/**
* 删除轮播图
* @auth true
* @menu true
* @login true
* @return void
*/
public function remove()
{
CmsBanner::mDelete('id');
}
/**
* 状态切换
* @auth true
* @menu true
* @login true
* @return void
*/
public function status()
{
CmsBanner::mSave($this->_vali([
'id.require' => '栏目ID不能为空',
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]), 'id');
}
public function sort()
{
CmsBanner::mSave($this->_vali([
'id.require' => '栏目ID不能为空',
'sort.require' => '排序值不能为空!',
'sort.number' => '排序必须为数字!',
'sort.between:0,9999' => '排序值必须为0~9999之间!',
]), 'id');
}
}

View File

@ -0,0 +1,111 @@
<?php
namespace plugin\cms\controller;
use plugin\cms\model\CmsCateArticle;
use plugin\cms\model\CmsCategory;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
class Category extends Controller
{
/**
* 栏目管理
* @auth true
* @menu true
* @login true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->title = '栏目列表';
CmsCategory::mQuery()->layTable(function () {
}, static function (QueryHelper $query) {
$query->equal('status')->like('name');
});
}
/**
* 添加栏目
* @auth true
* @menu true
* @login true
* @return void
*/
public function add()
{
$this->title = '添加栏目';
$this->_applyFormToken();
CmsCategory::mForm('form');
}
/**
* 编辑栏目
* @auth true
* @menu true
* @login true
* @return void
*/
public function edit()
{
$this->title = '编辑栏目';
$this->id = $this->request->param('id', 0, 'intval'); //当前栏目ID
$this->_applyFormToken();
CmsCategory::mForm('form');
}
/**
* 删除栏目
* @auth true
* @menu true
* @login true
* @return void
*/
public function remove()
{
CmsCategory::mDelete('id');
}
public function _delete_filter()
{
$id = $this->request->post('id');
$children_count = CmsCategory::where('pid', $id)->count();
if ($children_count > 0) {
$this->error("该栏目下有子栏目,不能删除!");
}
$articles_count = CmsCateArticle::where('cid', $id)->count();
if ($articles_count > 0) {
$this->error("该栏目下有文章,不能删除!");
}
return true;
}
/**
* 状态切换
* @auth true
* @menu true
* @login true
* @return void
*/
public function status()
{
CmsCategory::mSave($this->_vali([
'id.require' => '栏目ID不能为空',
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]), 'id');
}
public function sort()
{
CmsCategory::mSave($this->_vali([
'id.require' => '栏目ID不能为空',
'sort.require' => '排序值不能为空!',
'sort.number' => '排序必须为数字!',
'sort.between:0,9999' => '排序值必须为0~9999之间!',
]), 'id');
}
}

View File

@ -0,0 +1,12 @@
<?php
namespace plugin\cms\controller;
use think\admin\Controller;
class Index extends Controller
{
public function index()
{
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace plugin\cms\controller\api;
use plugin\cms\model\CmsArticle;
use think\admin\Controller;
class Article extends Controller
{
public function index() {
$categoryId = $this->request->get('cid');
$query = CmsArticle::mk()->where('status', 1);
if (is_numeric($categoryId)) {
$query->hasWhere('cateids', function ($subQuery) use ($categoryId) {
$subQuery->where('cid', $categoryId);
});
}
$articles = $query->field('id,title,thumb,author,from,view_count,publish_at')->order('sort asc,id desc')->paginate();
$this->success('获取文章列表', $articles);
}
public function info() {
$id = $this->request->get('id');
CmsArticle::mk()->where('id', $id)->inc('view_count');
$article = CmsArticle::mk()->where('id', $id)->find();
if (empty($article)) {
$this->error('文章不存在');
}
$this->success('获取文章详情', $article);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace plugin\cms\controller\api;
use plugin\cms\model\CmsBanner;
use think\admin\Controller;
class Banner extends Controller
{
public function index() {
$banners = CmsBanner::mk()->where('status', '=', 1)->field("id, image")->order('sort asc,id desc')->select();
$this->success('获取轮播图列表', $banners);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace plugin\cms\controller\api;
use plugin\cms\model\CmsCategory;
use think\admin\Controller;
class Category extends Controller
{
public function index() {
$categories = CmsCategory::mk()->where('status', 1)->order('sort asc,id desc')->field("id, name")->select();
$this->success('获取分类成功', $categories);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace plugin\cms\model;
use think\admin\Model;
class CmsArticle extends Model
{
public function category()
{
return $this->belongsToMany(CmsCategory::class, CmsCateArticle::class,
'cid', 'aid');
}
public function cateids()
{
return $this->hasMany(CmsCateArticle::class, 'aid');
}
public function getCateIdsAttr()
{
return $this->cateids->column('cid');
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace plugin\cms\model;
use think\admin\Model;
class CmsBanner extends Model
{
}

View File

@ -0,0 +1,17 @@
<?php
namespace plugin\cms\model;
use think\model\Pivot;
class CmsCateArticle extends Pivot
{
public function category()
{
return $this->belongsTo('CmsCategory', 'cid', 'id');
}
public function article()
{
return $this->belongsTo('CmsArtical', 'aid', 'id');
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace plugin\cms\model;
use think\admin\Model;
class CmsCategory extends Model
{
public static function getList()
{
return static::mk()->order('sort asc')->field('id,name')->where('status', 1)->select()->toArray();
}
public function articles()
{
return $this->hasManyThrough(CmsArticle::class, CmsCateArticle::class,
'cid', 'id', 'id', 'aid');
}
public function children()
{
return $this->hasMany(CmsCategory::class, 'pid', 'id');
}
public function parent()
{
return $this->belongsTo(CmsCategory::class, 'pid', 'id');
}
}

View File

@ -0,0 +1,88 @@
<form class="layui-form layui-card" action="{:sysuri()}" data-auto="true" method="post" autocomplete="off" data-table-id="articleTable">
<div class="layui-row">
<div class="layui-form-item">
<label class="layui-form-label">文章标题</label>
<div class="layui-input-block">
<input type="text" name="title" value='{$vo.title|default=""}' required placeholder="请输入文章标题" class="layui-input">
</div>
</div>
<div class="layui-row">
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">文章作者</label>
<div class="layui-input-block">
<input type="text" name="author" value='{$vo.author|default=""}' placeholder="请输入文章作者" class="layui-input">
</div>
</div>
</div>
<div class="layui-col-md6">
<div class="layui-form-item">
<label class="layui-form-label">文章来源</label>
<div class="layui-input-block">
<input type="text" name="source" value='{$vo.source|default=""}' placeholder="请输入文章来源" class="layui-input">
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">缩略图图片</label>
<div class="layui-input-block">
<input type="hidden" onchange="$(this).nextAll('img').attr('src', this.value)" name="thumb" value='{$vo.thumb|default=""}' placeholder="请上传缩略图图片" class="layui-input layui-hide">
<div class="layui-btn-container">
<a data-file="btn" data-type="bmp,png,jpeg,jpg" data-field="thumb" class="input-group-addon layui-btn layui-btn-active">
<i class="layui-icon layui-icon-upload"></i>
点击上传图片
</a>
<div class="layui-form-mid layui-word-aux">建议尺寸:750px*300px</div>
<a onclick="$('input[name=thumb]').val('').trigger('change')" class="layui-btn layui-btn-danger">
<i class="layui-icon layui-icon-close"></i>
点击清除图片
</a>
</div>
<img data-tips-image src='{$vo.thumb|default=""}' alt="image" style="width: 375px; height: 150px; object-fit: cover">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">文章分类</label>
<div class="layui-input-block">
{foreach $cates as $cate}
<label class="think-checkbox">
{if in_array($cate.id, $cate_ids)}
<input type="checkbox" checked name="cate_ids[]" value="{$cate.id}" lay-ignore>{$cate.name}
{else}
<input type="checkbox" name="cate_ids[]" value="{$cate.id}" lay-ignore>{$cate.name}
{/if}
</label>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">文章内容</label>
<div class="layui-input-block">
<textarea data-textarea class="layui-hide" name='content'>{$vo.content|default=""}</textarea>
</div>
</div>
</div>
{notempty name='id'}<input class="layui-input layui-hide" type='hidden' value='{$id}' name='id'>{/notempty}
<div class="layui-form-item text-center">
<button class="layui-btn layui-btn-normal" type='submit'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-close>取消编辑</button>
</div>
</form>
<script>
$(function () {
var editor = window.createEditor('[name="content"]');
if (typeof editor !== 'undefined') {
editor.on('change', function () {
$('[name="content"]').text(editor.getData());
});
} else {
$('[name="content"]').on('editor.init', function (event, myEditor) {
editor = myEditor;
myEditor.on('change', function () {
$('[name="content"]').text(editor.getData());
});
})
}
})
</script>

View File

@ -0,0 +1,108 @@
{extend name="table"}
{block name="button"}
<!--{if auth("add")}-->
<button type="button" class="layui-btn layui-btn-sm layui-btn-primary" data-modal='{:url("add")}' data-title="添加文章">添加</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="layui-tab">
<div class="layui-row">
<div class="layui-col">
<div class="article_list think-bg-white">
{include file='article/index_search'}
<table class="layui-hide" data-url="{$request->url()}" data-target-search="form.form-search" id="articleTable" lay-filter="articleTable"></table>
</div>
</div>
</div>
</div>
<script type="text/html" id="toolbar">
<!--{if auth("edit")}-->
<a data-modal="{:url('edit')}?id={{ d.id }}" data-height="80%" data-width="80%" class="layui-btn layui-btn-xs layui-btn-normal" title="修改">修改</a>
<!--{/if}-->
<!--{if auth("status")}-->
{{# if(d.status == 0){ }}
<a class="layui-btn layui-btn-xs layui-btn-normal" data-title="启用该轮播图" data-modal='{:url("status")}?id={{ d.id }}&status=1'> </a>
{{# } else { }}
<a class="layui-btn layui-btn-xs layui-btn-primary" data-title="隐藏该轮播图" data-modal='{:url("status")}?id={{ d.id }}&status=0'> </a>
{{# } }}
<!--{/if}-->
<!--{if auth("remove")}-->
<a data-confirm="确定要永久删除此文章吗?" data-action="{:url('remove')}" data-value="id#{{ d.id }}" data-csrf="{:systoken('remove')}" class="layui-btn layui-btn-xs layui-btn-danger" title="删除">删除</a>
<!--{/if}-->
</script>
{/block}
{block name='style'}
<style>
</style>
{/block}
{block name='script'}
<script>
$(function () {
$('#articleTable').layTable({
height: 'full-250',
toolbar: true,
sort: {field: 'sort', type: 'asc'},
title: '文章列表',
cols: [[
{type: 'numbers'},
{field: 'sort', title: '排序', width: 80, sort: true, edit: 'text'},
{field: 'thumb', title: '轮播图', width: 140, templet:function(item){
if (item.thumb) {
return '<img data-tips-image src="'+item.thumb+'" alt="image" style="width: 120px;height: 60px;">';
} else {
return '';
}
}},
{field:'title', title:'标题'},
{field:'cate_name', title:'栏目', width:120},
{field:'status', title:'状态', width:80, templet:function(item){
if(item.status == 0){
return '<span style="color:red;">隐藏</span>';
}else if(item.status == 1){
return '<span style="color:green;">显示</span>';
}
}},
{field:'view_count', title:'浏览数', width:100, sort: true},
{field:'like_count', title:'点赞数', width:100, sort: true},
{field:'share_count', title:'分享数', width:100, sort: true},
{fixed: 'right', title:'操作', toolbar: '#toolbar', width:180}
]],
page: true
})
layui.use(['table'], function(){
var table = layui.table,
layer = layui.layer;
table.on('edit(articleTable)', function(obj) {
if (obj.field == 'sort') {
if (obj.value == '') {
obj.value = obj.oldValue
return false;
} else {
$.form.load("{:url('sort')}", {sort: obj.value, id: obj.data.id}, 'post', function (ret) {
$('#tree-table').trigger('reload');
});
}
}
});
})
})
layui.use(['table'], function(){
var table = layui.table,
layer = layui.layer;
//监听工具条
table.on('tool(articleTable)', function(obj){
if(obj.event === 'del'){
layer.confirm('真的删除行么', function(index){
obj.del();
layer.close(index);
});
}
});
});
</script>
{/block}

View File

@ -0,0 +1,23 @@
<fieldset>
<legend>条件搜索</legend>
<form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">文章标题</label>
<div class="layui-input-inline">
<input name="title" value="{:input('get.title')}" placeholder="请输入文章标题" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">创建时间</label>
<div class="layui-input-inline">
<input data-date-range name="create_at" value="{:input('get.create_at')}" placeholder="请选择创建时间" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary" lay-submit lay-filter="searchData"><i class="layui-icon">&#xe615;</i> 搜 索</button>
</div>
</form>
</fieldset>

View File

@ -0,0 +1,33 @@
<form class="layui-form layui-card" action="{:sysuri()}" data-auto="true" method="post" autocomplete="off" data-table-id="articleTable">
<div class="layui-row layui-col-space15">
<div class="layui-form-item">
<label class="layui-form-label">轮播图名称</label>
<div class="layui-input-block">
<input type="text" name="name" value='{$vo.name|default=""}' required placeholder="请输入轮播图名称" class="layui-input">
<div class="layui-form-mid layui-word-aux">仅用于后台展示</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">轮播图图片</label>
<div class="layui-input-block">
<input type="hidden" name="image" data-size="1" value='{$vo.image|default=""}' required placeholder="请上传轮播图图片" class="layui-input layui-hide">
<div class="layui-form-mid layui-word-aux">建议尺寸:750*300</div>
</div>
</div>
</div>
{notempty name='id'}<input class="layui-input layui-hide" type='hidden' value='{$id}' name='id'>{/notempty}
<div class="layui-form-item text-center">
<button class="layui-btn layui-btn-normal" type='submit'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-close>取消编辑</button>
</div>
</form>
<script>
$('[name="image"]').uploadOneImage()
layui.use(['form'], function () {
var layer = layui.layer,
form = layui.form;
form.render();
});
</script>

View File

@ -0,0 +1,87 @@
{extend name="table"}
{block name="button"}
<!--{if auth("add")}-->
<button type="button" class="layui-btn layui-btn-sm layui-btn-primary" data-modal='{:url("add")}' data-title="添加轮播图">添加</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="layui-tab layui-tab-card think-bg-white">
<div class="layui-tab-content think-box-shadow">
{include file='banner/index_search'}
<table class="layui-table margin-top-10" data-url="{$request->url()}" data-target-search="form.form-search" lay-skin="line" id="bannerTable"></table>
</div>
</div>
<script type="text/html" id="toolbar">
<!--{if auth("edit")}-->
<a data-modal="{:url('edit')}?id={{ d.id }}" class="layui-btn layui-btn-xs layui-btn-normal" title="修改">修改</a>
<!--{/if}-->
<!--{if auth("status")}-->
{{# if(d.status == 0){ }}
<a class="layui-btn layui-btn-xs layui-btn-normal" data-title="启用该轮播图" data-modal='{:url("status")}?id={{ d.id }}&status=1'> </a>
{{# } else { }}
<a class="layui-btn layui-btn-xs layui-btn-primary" data-title="隐藏该轮播图" data-modal='{:url("status")}?id={{ d.id }}&status=0'> </a>
{{# } }}
<!--{/if}-->
<!--{if auth("remove")}-->
<a data-confirm="确定要永久删除此轮播图吗?" data-action="{:url('remove')}" data-value="id#{{ d.id }}" data-csrf="{:systoken('remove')}" class="layui-btn layui-btn-xs layui-btn-danger" title="删除">删除</a>
<!--{/if}-->
</script>
{/block}
{block name='style'}
<style>
</style>
{/block}
{block name='script'}
<script>
$(function () {
$('#bannerTable').layTable({
height: 'full-240',
toolbar: true,
sort: {field: 'sort', type: 'asc'},
title: '轮播图列表',
cols: [[
{type: 'numbers'},
{field: 'sort', title: '排序', width: 80, sort: true, edit: 'text'},
{field: 'image', title: '轮播图', width: 140, templet:function(item){
return '<img data-tips-image src="'+item.image+'" alt="image" style="width: 120px;height: 60px;">';
}},
{field: 'name', title: '轮播图名称', minWidth: 120},
{field: 'status', title: '状态', width: 60,templet:function(item){
if(item.status == 0){
return '<span style="color:red;">隐藏</span>';
}else if(item.status == 1){
return '<span style="color:green;">显示</span>';
}
}},
{field: 'create_at', title: '创建时间', width: 150},
{align: 'center', toolbar: '#toolbar', title: '操作', width: 240}
]]
})
layui.use(['table'], function(){
var table = layui.table,
layer = layui.layer;
table.on('edit(bannerTable)', function(obj) {
if (obj.field == 'sort') {
if (obj.value == '') {
obj.value = obj.oldValue
return false;
} else {
$.form.load("{:url('sort')}", {sort: obj.value, id: obj.data.id}, 'post', function (ret) {
$('#bannerTable').trigger('reload');
});
}
}
});
})
})
</script>
{/block}

View File

@ -0,0 +1,16 @@
<fieldset>
<legend>条件搜索</legend>
<form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">名称</label>
<div class="layui-input-inline">
<input name="name" value="{:input('get.name')}" placeholder="请输入名称" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary" lay-submit lay-filter="searchData"><i class="layui-icon">&#xe615;</i> 搜 索</button>
</div>
</form>
</fieldset>

View File

@ -0,0 +1,99 @@
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post" autocomplete="off">
<div class="layui-card-body">
<div class="layui-row layui-col-space15">
<!--<div class="layui-form-item">
<label class="layui-form-label label-required-next">所属分类</label>
<div class="layui-input-block">
<select class="layui-input" name="pid" required>
<option value="0">作为一级分类</option>
</select>
</div>
</div>-->
<div class="layui-form-item">
<label class="layui-form-label">栏目名称</label>
<div class="layui-input-block">
<input type="text" name="name" value='{$vo.name|default=""}' required placeholder="请输入栏目名称" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">栏目描述</label>
<div class="layui-input-block">
<textarea name="description" placeholder="请输入描述" class="layui-textarea">{$vo.description|default=""}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">是否为外链</label>
<div class="layui-input-block">
<div class="layui-col-space15" style="content: none">
<div class="layui-col-xs2">
{foreach $_YES_OR_NO as $k=>$v}
<!--{if (!isset($vo.is_link) and $k eq '0') or (isset($vo.is_link) and $vo.is_link eq $k)}-->
<input type="radio" name="is_link" lay-filter="islink_filter" checked value="{$k}" title="{$v}">
<!--{else}-->
<input type="radio" name="is_link" lay-filter="islink_filter" value="{$k}" title="{$v}">
<!--{/if}-->
{/foreach}
</div>
<div class="layui-col-xs8" id="category_link_url" style="display: none;">
<input type="text" name="link_url" value='{$vo.link_url|default=""}' pattern="^(http:\/\/|^https:\/\/|^\/\/)((\w|=|\?|\.|\/|&|-)+)" placeholder="请输入跳转链接地址" class="layui-input">
</div>
</div>
</div>
</div>
</div>
<div class="hr-line-dashed"></div>
{notempty name='vo.id'}<input type='hidden' value='{$vo.id}' name='id'>{/notempty}
<div class="layui-form-item text-center">
<button class="layui-btn layui-btn-normal" type='submit'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-close>取消编辑</button>
</div>
</div>
</form>
<style>
.layui-tab-item{ min-height: 400px;}
.input-group-addon {
top: 0;
right: 0;
color: #eee;
width: 25px;
padding: 7px;
position: absolute;
margin-top: -1px;
text-align: center;
background: #393D49;
}
</style>
<script>
layui.form.render();
layui.use(['layer', 'jquery', 'form','element'], function () {
var layer = layui.layer,
$ = layui.jquery,
form = layui.form,
element = layui.element;
//监听是否为跳转链接的选择变化
form.on("radio(islink_filter)", function (data) {
if(parseInt(data.value)){
$('#category_link_url').show();
}else{
$('#category_link_url').hide();
}
});
//监听指定开关
form.on('switch(urlpath_filter)', function(data){
if(this.checked){
$(this).parent('.layui-input-block').prev().removeClass('label-required-next');
$('input[name="url_path"]').next('span').hide();
$('input[name="url_path"]').removeAttr('required').attr('disabled',true);
}else{
$(this).parent('.layui-input-block').prev().addClass('label-required-next');
$('input[name="url_path"]').next('span').show();
$('input[name="url_path"]').removeAttr('disabled').attr('required',true);
}
});
});
</script>

View File

@ -0,0 +1,84 @@
{extend name="table"}
{block name="button"}
<!--{if auth("add")}-->
<button data-modal='{:url("add")}' data-title="添加栏目" class='layui-btn layui-btn-sm layui-btn-primary'>添加栏目</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="layui-tab layui-tab-card think-bg-white">
<div class="layui-tab-content think-box-shadow">
{include file='category/index_search'}
<table class="layui-table margin-top-10" data-url="{$request->url()}" data-target-search="form.form-search" lay-skin="line" id="categoryTable"></table>
</div>
</div>
<script type="text/html" id="toolbar">
<!--{if auth("edit")}-->
<a data-modal="{:url('edit')}?id={{ d.id }}" class="layui-btn layui-btn-xs layui-btn-normal" title="修改">修改</a>
<!--{/if}-->
<!--{if auth("status")}-->
{{# if(d.status == 0){ }}
<a class="layui-btn layui-btn-xs layui-btn-normal" data-title="显示分类" data-modal='{:url("status")}?id={{ d.id }}&status=1'> </a>
{{# } else { }}
<a class="layui-btn layui-btn-xs layui-btn-primary" data-title="隐藏分类" data-modal='{:url("status")}?id={{ d.id }}&status=0'> </a>
{{# } }}
<!--{/if}-->
<!--{if auth("remove")}-->
<a data-confirm="确定要永久删除此分类吗?" data-action="{:url('remove')}" data-value="id#{{ d.id }}" data-csrf="{:systoken('remove')}" class="layui-btn layui-btn-xs layui-btn-danger" title="删除">删除</a>
<!--{/if}-->
</script>
{/block}
{block name='style'}
<style>
</style>
{/block}
{block name='script'}
<script>
$(function () {
$('#categoryTable').layTable({
height: 'full-240',
toolbar: true,
sort: {field: 'sort', type: 'asc'},
cols: [[
{type: 'numbers'},
{field: 'sort', title: '排序', width: 80, sort: true, edit: 'text'},
{field: 'name', title: '栏目名称', minWidth: 165},
{field: 'status', title: '状态', width: 60,templet:function(item){
if(item.status == 0){
return '<span style="color:red;">隐藏</span>';
}else if(item.status == 1){
return '<span style="color:green;">显示</span>';
}
}},
{field: 'description', title: '分类描述', width: 150},
{field: 'is_link', title: '是否为外链', templet: '<p>{{d.is_link?"是":"否"}}</p>', align: 'center', width: 90},
{field: 'create_at', title: '创建时间', width: 150},
{align: 'center', toolbar: '#toolbar', title: '操作', width: 240}
]]
})
layui.use(['table'], function(){
var table = layui.table,
layer = layui.layer;
table.on('edit(categoryTable)', function(obj) {
if (obj.field == 'sort') {
if (obj.value == '') {
obj.value = obj.oldValue
return false;
} else {
$.form.load("{:url('sort')}", {sort: obj.value, id: obj.data.id}, 'post', function (ret) {
$('#categoryTable').trigger('reload');
});
}
}
});
})
})
</script>
{/block}

View File

@ -0,0 +1,16 @@
<fieldset>
<legend>条件搜索</legend>
<form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">分类名称</label>
<div class="layui-input-inline">
<input name="name" value="{:input('get.name')}" placeholder="请输入分类名称" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary" lay-submit lay-filter="searchData"><i class="layui-icon">&#xe615;</i> 搜 索</button>
</div>
</form>
</fieldset>

View File

@ -0,0 +1,11 @@
<div class="layui-card layui-bg-gray">
{block name='style'}{/block}
{notempty name='title'}
<div class="layui-card-header notselect">
<span class="layui-icon layui-icon-next font-s10 color-desc margin-right-5"></span>{$title|default=''}
<div class="pull-right">{block name='button'}{/block}</div>
</div>
{/notempty}
<div class="layui-card-body">{block name='content'}{/block}</div>
{block name='script'}{/block}
</div>

View File

@ -0,0 +1,23 @@
<div class="layui-card">
{block name='style'}{/block}
{block name='header'}
{notempty name='title'}
<div class="layui-card-header">
<span class="layui-icon font-s10 color-desc ta-mr-5">&#xe65b;</span>{$title|lang}
<div class="pull-right">{block name='button'}{/block}</div>
</div>
{/notempty}
{/block}
<div class="layui-card-line"></div>
<div class="layui-card-body">
<div class="layui-card-table">
{notempty name='showErrorMessage'}
<div class="think-box-notify" type="error">
<b>系统提示:</b><span>{$showErrorMessage|raw}</span>
</div>
{/notempty}
{block name='content'}{/block}
</div>
</div>
{block name='script'}{/block}
</div>

View File

@ -0,0 +1,41 @@
{
"type": "think-admin-plugin",
"name": "jerryyan/think-plugs-points-mall",
"license": "WTFPL",
"homepage": "https://thinkadmin.top",
"description": "Points Mall Plugin for ThinkAdmin",
"authors": [
{
"name": "Jerry Yan",
"email": "jerryyan0912@qq.com"
}
],
"require": {
"php": ">7.1"
},
"autoload": {
"psr-4": {
"plugin\\points_mall\\": "src"
}
},
"extra": {
"config": {
"type": "module",
"name": "积分商城",
"document": "https://thinkadmin.top/plugin/think-plugs-wechat.html",
"description": "积分商城"
},
"think": {
"services": [
"plugin\\points_mall\\Service"
]
}
},
"minimum-stability": "dev",
"config": {
"sort-packages": true,
"allow-plugins": {
"zoujingli/think-install": true
}
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace plugin\points_mall;
use think\admin\Plugin;
use think\facade\View;
class Service extends Plugin
{
protected $appName = '积分商城';
protected $appCode = 'points-mall';
protected $package = 'jerryyan/think-plugs-points-mall';
public static function register(): void
{
View::assign("_YES_OR_NO", [
'1' => '是',
'0' => '否'
]);
}
public static function menu(): array
{
$code = app(static::class)->appCode;
return [
[
'name' => '积分商城',
'subs' => [
['name' => '商品管理', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/goods/index"],
['name' => '订单管理', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/order/index"],
['name' => '用户积分', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/point/index"],
]
]
];
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace plugin\points_mall\controller;
use think\admin\Controller;
/**
* 商品管理
*/
class Goods extends Controller
{
/**
* 商品管理
* @auth true
* @menu true
* @return void
*/
public function index()
{
$this->title = '商品管理';
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace plugin\points_mall\controller;
use think\admin\Controller;
/**
* 积分情况
*/
class Point extends Controller
{
/**
* 积分情况
* @auth true
* @menu true
* @return void
*/
public function index()
{
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace plugin\points_mall\controller\api;
use plugin\account\controller\api\Auth as AuthController;
use think\exception\HttpResponseException;
class Auth extends AuthController
{
/**
* 控制器初始化
* @return void
*/
protected function initialize()
{
try {
parent::initialize();
$this->checkUserStatus(false);
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
$this->error($exception->getMessage());
}
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace plugin\points_mall\controller\api\auth;
use plugin\points_mall\controller\api\Auth;
use plugin\points_mall\model\PointsMallUserPointLog;
use plugin\points_mall\service\UserPointService;
class UserPoint extends Auth
{
public function myPoint() {
$point = UserPointService::getUserPoint($this->usid);
$this->success('获取用户积分', [
'point' => $point
]);
}
public function myPointLog() {
$page = PointsMallUserPointLog::mk()->where('uid', $this->usid)->order('create_at desc')->paginate();
$this->success('获取积分记录', $page);
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace plugin\points_mall\model;
use plugin\account\model\PluginAccountBind;
use think\admin\Model;
class PointsMallUserPoint extends Model
{
protected $pk = 'uid';
public function log()
{
return $this->hasMany(PointsMallUserPointLog::class, 'uid');
}
public function user()
{
return $this->belongsTo(PluginAccountBind::class, 'uid');
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace plugin\points_mall\model;
use think\admin\Model;
class PointsMallUserPointLog extends Model
{
protected $pk = 'uid';
}

View File

@ -0,0 +1,35 @@
<?php
namespace plugin\points_mall\service;
use plugin\points_mall\model\PointsMallUserPoint;
use plugin\points_mall\model\PointsMallUserPointLog;
class UserPointService
{
public static function getUserPoint($uid)
{
$point = PointsMallUserPoint::mk()->where('uid', $uid)->value('point');
if ($point === null) {
$point = 0;
PointsMallUserPoint::mk()->save(['uid' => $uid, 'point' => $point]);
}
return $point;
}
public static function addUserPoint($uid, $point, $reason)
{
$userPoint = PointsMallUserPoint::mk()->where('uid', $uid)->find();
if (empty($userPoint)) {
$userPoint = PointsMallUserPoint::mk()->create(['uid' => $uid, 'point' => 0]);
}
$userPoint->startTrans();
$userPoint->inc('point', $point);
$userPoint->log()->save([
'point' => $point,
'reason' => $reason,
]);
$userPoint->save();
$userPoint->commit();
}
}

View File

@ -0,0 +1,11 @@
<div class="layui-card layui-bg-gray">
{block name='style'}{/block}
{notempty name='title'}
<div class="layui-card-header notselect">
<span class="layui-icon layui-icon-next font-s10 color-desc margin-right-5"></span>{$title|default=''}
<div class="pull-right">{block name='button'}{/block}</div>
</div>
{/notempty}
<div class="layui-card-body">{block name='content'}{/block}</div>
{block name='script'}{/block}
</div>

View File

@ -0,0 +1,23 @@
<div class="layui-card">
{block name='style'}{/block}
{block name='header'}
{notempty name='title'}
<div class="layui-card-header">
<span class="layui-icon font-s10 color-desc ta-mr-5">&#xe65b;</span>{$title|lang}
<div class="pull-right">{block name='button'}{/block}</div>
</div>
{/notempty}
{/block}
<div class="layui-card-line"></div>
<div class="layui-card-body">
<div class="layui-card-table">
{notempty name='showErrorMessage'}
<div class="think-box-notify" type="error">
<b>系统提示:</b><span>{$showErrorMessage|raw}</span>
</div>
{/notempty}
{block name='content'}{/block}
</div>
</div>
{block name='script'}{/block}
</div>

View File

@ -0,0 +1,41 @@
{
"type": "think-admin-plugin",
"name": "jerryyan/think-plugs-ticket",
"license": "WTFPL",
"homepage": "https://thinkadmin.top",
"description": "Ticket Plugin for ThinkAdmin",
"authors": [
{
"name": "Jerry Yan",
"email": "jerryyan0912@qq.com"
}
],
"require": {
"php": ">7.1"
},
"autoload": {
"psr-4": {
"plugin\\ticket\\": "src"
}
},
"extra": {
"config": {
"type": "module",
"name": "工单管理",
"document": "https://thinkadmin.top/plugin/think-plugs-wechat.html",
"description": "工单管理"
},
"think": {
"services": [
"plugin\\ticket\\Service"
]
}
},
"minimum-stability": "dev",
"config": {
"sort-packages": true,
"allow-plugins": {
"zoujingli/think-install": true
}
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace plugin\ticket;
use think\admin\Plugin;
class Service extends Plugin
{
protected $appName = '工单管理';
protected $appCode = 'ticket';
protected $package = 'jerryyan/think-plugs-ticket';
public static function register(): void
{
}
public static function menu(): array
{
$code = app(static::class)->appCode;
return [
[
'name' => '工单管理',
'subs' => [
['name' => '工单列表', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/ticket/index"],
['name' => '工单分类', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/type/index"],
]
]
];
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace plugin\ticket\controller;
use plugin\ticket\model\TicketReply;
use plugin\ticket\model\TicketTicket;
use plugin\ticket\model\TicketType;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
/**
* 工单管理
*/
class Ticket extends Controller
{
/**
* 工单列表
* @auth true
* @menu true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->title = '工单中心';
$this->type_list = TicketType::getList();
$this->user_id = $this->request->session('user')['id'];
TicketTicket::mQuery()->layTable(function () {
}, function (QueryHelper $query) {
$query->like(['title|content|contact_name|ticket_address|contact_phone#keyword'])
->dateBetween(['create_at', 'last_activity_at'])
->equal(['status', 'type_id']);
$query->with(['user'])->append(['imgs_arr', 'status_text', 'type_name', 'last_reply']);
});
}
/**
* 添加工单
* @auth true
* @menu true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function add()
{
$this->title = '添加工单';
$this->types = TicketType::mk()->scope('active')->select();
TicketTicket::mForm('form');
}
public function _form_filter(&$data)
{
if ($this->request->isPost()) {
$data['uid'] = 0;
}
$data['status'] = 0;
}
/**
* 查看工单
* @auth true
* @menu true
* @return void
*/
public function detail()
{
$this->title = '工单详情';
['id' => $id] = $this->_vali(['id.require' => '请指定工单ID!']);
$this->vo = TicketTicket::mk()->with(['user', 'type', 'reply'])->append(['imgs_arr', 'status_text', 'type_name', 'last_reply'])->find($id);
$this->fetch();
}
/**
* 编辑工单
* @auth true
* @menu true
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function edit()
{
$this->title = '编辑工单';
$this->types = TicketType::mk()->scope('active')->select();
TicketTicket::mForm('form');
}
/**
* 删除工单
* @auth true
* @menu true
* @return void
*/
public function remove()
{
TicketTicket::mDelete();
}
public function reply()
{
$this->title = '回复工单';
[
'ticket_id'=>$ticket_id,
'id'=>$id
] = $this->_vali([
'ticket_id.require'=>'请指定工单ID!',
'id.require'=>'请指定回复ID!'
]);
$this->vo = TicketTicket::mk()->with(['user', 'type'])->append(['imgs_arr', 'status_text', 'type_name'])->find($ticket_id);
TicketReply::mForm('reply');
}
public function _reply_form_filter(&$data)
{
if ($this->request->isPost()) {
$this->_applyFormToken();
$data['type'] = 1;
$adminInfo = $this->request->session('user');
$data['user_id'] = $adminInfo['id'];
$data['username'] = $adminInfo['nickname'];
$data['contact'] = $adminInfo['contact_phone'];
}
}
}

View File

@ -0,0 +1,90 @@
<?php
namespace plugin\ticket\controller;
use plugin\ticket\model\TicketTicket;
use plugin\ticket\model\TicketType;
use think\admin\Controller;
use think\admin\helper\QueryHelper;
/**
* 工单类型管理
*/
class Type extends Controller
{
/**
* 工单类型列表
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function index()
{
$this->title = '工单类型';
TicketType::mQuery()->layTable(function () {
}, function (QueryHelper $query) {
$query->withCount(['tickets']);
$query->like(['name'])->equal(['status']);
});
}
/**
* 添加工单类型
* @return void
*/
public function add()
{
TicketType::mForm('form');
}
/**
* 编辑工单类型
* @return void
*/
public function edit()
{
TicketType::mForm('form');
}
/**
* 修改工单类型状态
* @return void
*/
public function status()
{
TicketType::mSave($this->_vali([
'id.require' => '类型ID不能为空',
'status.in:0,1' => '状态值范围异常!',
'status.require' => '状态值不能为空!',
]), 'id');
}
/**
* 修改工单类型排序
* @return void
*/
public function sort()
{
TicketType::mSave($this->_vali([
'id.require' => '文章ID不能为空',
'sort.require' => '排序值不能为空!',
'sort.number' => '排序必须为数字!',
'sort.between:0,9999' => '排序值必须为0~9999之间!',
]), 'id');
}
/**
* 删除工单类型
* @return void
*/
public function remove()
{
['id' => $id] = $this->_vali(['id.require' => '请指定工单类型ID!']);
$count = TicketTicket::mk()->scope('avail')->where('type_id', '=', $id)->count();
if ($count > 0) {
$this->error('工单类型删除失败,请先删除该类型下的工单!');
}
TicketType::mDelete();
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace plugin\ticket\model;
use think\admin\Model;
use think\admin\model\SystemUser;
class TicketReply extends Model
{
protected $append = ['type_name'];
public function ticket()
{
return $this->belongsTo(TicketTicket::class, 'ticket_id');
}
public function user()
{
return $this->belongsTo(SystemUser::class, 'user_id');
}
public function getTypeNameAttr($value, $data)
{
switch ($data['type']) {
case 0: return '流转';
case 1: return '回复';
case 9: return '退回';
default: return '其他';
}
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace plugin\ticket\model;
use plugin\account\model\PluginAccountBind;
use think\admin\Model;
class TicketTicket extends Model
{
protected $append = ['status_text', 'type_name'];
public function type()
{
return $this->belongsTo(TicketType::class, 'type_id');
}
public function user()
{
return $this->belongsTo(PluginAccountBind::class, 'uid');
}
public function reply()
{
return $this->hasMany(TicketReply::class, 'ticket_id')->order("create_at", 'asc');
}
public function getImgsArrAttr($value, $data)
{
return str2arr($data['imgs'] ?: '', '|');
}
public function getLastReplyAttr($value, $data)
{
return $this->reply()->order('create_at', 'desc')->find();
}
public function getTypeNameAttr($value, $data)
{
$type = $this->type()->find();
if (!empty($type)) {
return $type['name'];
} else {
return '未知';
}
}
public function getStatusTextAttr($value, $data)
{
if (!empty($this->getStatusList()[$data['status']])) {
return $this->getStatusList()[$data['status']];
} else {
return '未知';
}
}
private function getStatusList()
{
return [
-1 => '已关闭',
0 => '待处理',
1 => '已作处理',
];
}
public function scopeAvail($query)
{
return $query->where('status', '>=', '0');
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace plugin\ticket\model;
use think\admin\Model;
class TicketType extends Model
{
public static function getList()
{
return self::mk()->scope('active')->order('sort', 'asc')->field('name, id')->select();
}
public function tickets()
{
return $this->hasMany(TicketTicket::class, 'type_id');
}
public function scopeActive($query)
{
return $query->where('status', '=', '1');
}
}

View File

@ -0,0 +1,11 @@
<div class="layui-card layui-bg-gray">
{block name='style'}{/block}
{notempty name='title'}
<div class="layui-card-header notselect">
<span class="layui-icon layui-icon-next font-s10 color-desc margin-right-5"></span>{$title|default=''}
<div class="pull-right">{block name='button'}{/block}</div>
</div>
{/notempty}
<div class="layui-card-body">{block name='content'}{/block}</div>
{block name='script'}{/block}
</div>

View File

@ -0,0 +1,23 @@
<div class="layui-card">
{block name='style'}{/block}
{block name='header'}
{notempty name='title'}
<div class="layui-card-header">
<span class="layui-icon font-s10 color-desc ta-mr-5">&#xe65b;</span>{$title|lang}
<div class="pull-right">{block name='button'}{/block}</div>
</div>
{/notempty}
{/block}
<div class="layui-card-line"></div>
<div class="layui-card-body">
<div class="layui-card-table">
{notempty name='showErrorMessage'}
<div class="think-box-notify" type="error">
<b>系统提示:</b><span>{$showErrorMessage|raw}</span>
</div>
{/notempty}
{block name='content'}{/block}
</div>
</div>
{block name='script'}{/block}
</div>

View File

@ -0,0 +1,65 @@
<div class="layui-card">
<div class="layui-card-header">工单内容</div>
<div class="layui-card-body">
<div class="layui-row">
工单编号:{$vo.id|default=""}
</div>
<div class="layui-row">
工单标题:{$vo.title|default=""}
</div>
<div class="layui-row">
反馈人信息:{$vo.contact_name|default="未填写名称"}
联系方式:{$vo.contact_phone|default="未填写联系方式"}
联系地址:{$vo.contact_address|default="未填写地址"}
</div>
<div class="layui-row">
工单类型:{$vo.type_name|default=""}
</div>
<div class="layui-row">
工单地址:{$vo.ticket_address|default=""}
</div>
<div class="layui-row">
工单内容:{$vo.content|default=""}
</div>
<div class="layui-row">
工单图片:
<div>
{foreach $vo.imgs_arr as $img}
<img data-tips-image src="{$img}" alt="image" style="height: 100px;">
<span>&nbsp;</span>
{/foreach}
</div>
</div>
</div>
</div>
<div class="layui-card">
<div class="layui-card-header">工单流转详情</div>
<div class="layui-card-body">
<table id="replyTable" class="layui-table">
<thead>
<tr>
<th lay-data='{field: "username", width: "120px"}'>工单处理人</th>
<th lay-data='{field: "type_name", width: "120px"}'>处理类型</th>
<th lay-data='{field: "create_at", width: "180px"}'>工单处理时间</th>
<th lay-data='{field: "content", minWidth: "200px"}'>处理内容</th>
</tr>
</thead>
<tbody>
{notempty name='vo.reply'}
{foreach $vo.reply as $reply}
<tr>
<td>{$reply.username|default=""}</td>
<td>{$reply.type_name|default=""}</td>
<td>{$reply.create_at|default=""}</td>
<td>{$reply.content|default=""}</td>
</tr>
{/foreach}
{else}
<tr>
<td colspan="999" class="text-center">暂无工单流转记录</td>
</tr>
{/notempty}
</tbody>
</table>
</div>
</div>

View File

@ -0,0 +1,44 @@
<form class="layui-form layui-card" action="{:sysuri()}" data-auto="true" method="post" autocomplete="off" data-table-id="ticketTable">
<h1 style="color: red; text-align: center; padding: 20px; border: 1px solid red; margin-bottom: 20px">
原则上不允许私自创建或修改工单!!!
</h1>
<div class="layui-row">
<div class="layui-form-item">
<label class="layui-form-label">工单标题</label>
<div class="layui-input-block">
<input type="text" name="title" value='{$vo.title|default=""}' required placeholder="请输入工单标题" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">工单分类</label>
<div class="layui-input-block">
<select class="layui-select" name="type_id" required>
{foreach $types as $type}
<option value='{$type.id}'>{$type.name}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">工单内容</label>
<div class="layui-input-block">
<textarea data-textarea class="layui-textarea" name='content' required>{$vo.content|default=""}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">工单图片</label>
<div class="layui-input-block">
<input type="hidden" value='{$vo.imgs|default=""}' name="imgs">
</div>
</div>
</div>
{notempty name='id'}<input class="layui-input layui-hide" type='hidden' value='{$id}' name='id'>{/notempty}
<div class="layui-form-item text-center">
<button class="layui-btn layui-btn-normal" type='submit'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-close>取消编辑</button>
</div>
</form>
<script>
$('[name="imgs"]').uploadMultipleImage()
</script>

View File

@ -0,0 +1,85 @@
{extend name="table"}
{block name="button"}
<!--{if auth("add")}-->
<button type="button" class="layui-btn layui-btn-sm layui-btn-disabled" data-modal='{:url("add")}' data-title="添加工单">添加工单</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="layui-tab">
<div class="layui-row">
<div class="layui-col">
<div class="article_list think-bg-white">
{include file='ticket/index_search'}
<table class="layui-hide" data-url="{$request->url()}" data-target-search="form.form-search" id="ticketTable" lay-filter="ticketTable"></table>
</div>
</div>
</div>
</div>
<script type="text/html" id="toolbar">
<!--{if auth("detail")}-->
<a data-modal="{:url('detail')}?id={{ d.id }}" data-height="90%" data-width="60%" class="layui-btn layui-btn-xs layui-btn-normal" title="详情">详情</a>
<!--{/if}-->
<!--{if auth("move")}-->
<a data-modal="{:url('move')}?ticket_id={{ d.id }}" data-height="80%" data-width="80%" class="layui-btn layui-btn-xs" title="流转">流转</a>
<!--{/if}-->
<!--{if auth("reply")}-->
{{# if (d.last_reply && d.last_reply.user_id == {$user_id} && d.last_reply.status == 0) { }}
<a data-modal="{:url('reply')}?id={{ d.last_reply.id }}&ticket_id={{ d.id }}" data-height="80%" data-width="80%" class="layui-btn layui-btn-xs" title="回复">回复</a>
{{# }; }}
<!--{/if}-->
<!--{if auth("edit")}-->
<a data-modal="{:url('edit')}?id={{ d.id }}" data-height="80%" data-width="80%" class="layui-btn layui-btn-xs layui-btn-danger" title="修改工单">修改</a>
<!--{/if}-->
<!--{if auth("remove")}-->
<a data-confirm="原则上不允许主动删除用户工单,是否继续删除?" data-action="{:url('remove')}" data-value="id#{{ d.id }}" class="layui-btn layui-btn-xs layui-btn-danger" title="删除">删除</a>
<!--{/if}-->
</script>
{/block}
{block name='style'}
<style>
</style>
{/block}
{block name='script'}
<script>
$(function () {
$('#ticketTable').layTable({
height: 'full-250',
toolbar: true,
sort: {field: 'create_at', type: 'desc'},
title: '工单列表',
cols: [[
{field: 'create_at', title: '工单创建时间', width: 160, sort: true},
{field:'title', title:'标题', width:160},
{field:'type_name', title:'工单类型', width:120},
{field:'content', title:'工单内容', minWidth:100},
{field:'ticket_address', title:'工单地址', width:100},
{field: 'imgs', title: '工单图片', width: 100, templet:function(item){
if (item.imgs) {
return item.imgs.split("|").map((img) => {
return laytpl('<a target="_blank" data-tips-hover data-tips-image="{{d}}"><i class="layui-icon layui-icon-picture"></i></a>').render(img)
}).concat(' ')
} else {
return '';
}
}},
{field:'status', title:'状态', width:80, templet:function(item){
if(item.status == 0){
return '<span style="color:red;">正在处理</span>';
}else if(item.status == 1){
return '<span style="color:green;">已处理</span>';
}else{
return '<span style="color:gray;">已关闭</span>';
}
}},
{fixed: 'right', title:'操作', toolbar: '#toolbar', width:200}
]],
page: true
})
})
</script>
{/block}

View File

@ -0,0 +1,50 @@
<fieldset>
<legend>条件搜索</legend>
<form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">关键字</label>
<div class="layui-input-inline">
<input name="keyword" value="{:input('get.keyword')}" placeholder="标题、内容、地址、当事人、电话" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">工单类型</label>
<div class="layui-input-inline">
<select class="layui-select" name="type_id">
<option value=''>-- 全部 --</option>
{foreach $type_list as $type}
<option value='{$type.id}'>{$type.name}</option>
{/foreach}
</select>
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">工单状态</label>
<div class="layui-input-inline">
<select class="layui-select" name="status">
<option value=''>-- 全部 --</option>
<option value='0'>正在处理</option>
<option value='1'>已处理</option>
<option value='-1'>已取消</option>
</select>
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">创建时间</label>
<div class="layui-input-inline">
<input data-date-range name="create_at" value="{:input('get.create_at')}" placeholder="请选择创建时间" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<label class="layui-form-label">最后操作时间</label>
<div class="layui-input-inline">
<input data-date-range name="last_activity_at" value="{:input('get.last_activity_at')}" placeholder="请选择最后操作时间" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary" lay-submit lay-filter="searchData"><i class="layui-icon">&#xe615;</i> 搜 索</button>
</div>
</form>
</fieldset>

View File

@ -0,0 +1,43 @@
<form class="layui-form layui-card margin-top-10 padding-10" action="{:sysuri()}" data-auto="true" method="post" autocomplete="off" data-table-id="ticketTable">
<input class="layui-input layui-hide" type='hidden' value="{:input('get.id')}" name='id'>
<input class="layui-input layui-hide" type='hidden' value="{:input('get.ticket_id')}" name='ticket_id'>
<input class="layui-input layui-hide" type='hidden' value='{:systoken("reply")}' name='_token_'>
<div class="layui-form-item">
<label class="layui-form-label">回复内容</label>
<div class="layui-input-block">
<textarea data-textarea class="layui-textarea" name='content' required></textarea>
<div class="layui-word-aux">此回复对外可见,请认真回复。</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">回复图片</label>
<div class="layui-input-block">
<input class="layui-hide" name='imgs'>
<div class="layui-word-aux">此图片对外可见,请认真回复。</div>
</div>
</div>
<hr>
<div class="layui-form-item">
<label class="layui-form-label">内部回复内容</label>
<div class="layui-input-block">
<textarea data-textarea class="layui-textarea" name='inner_content'></textarea>
<div class="layui-word-aux">此回复仅系统内部可见。</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">内部回复图片</label>
<div class="layui-input-block">
<input class="layui-hide" name='inner_imgs'>
<div class="layui-word-aux">此图片仅系统内部可见。</div>
</div>
</div>
<div class="layui-form-item text-center">
<button class="layui-btn layui-btn-normal" type='submit'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-close>取消编辑</button>
</div>
</form>
<script>
$('[name="imgs"]').uploadMultipleImage()
$('[name="inner_imgs"]').uploadMultipleImage()
</script>
{include file="ticket/detail"}

View File

@ -0,0 +1,63 @@
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post" autocomplete="off" data-table-id="typeTable">
<div class="layui-card-body">
<div class="layui-row layui-col-space15">
<div class="layui-form-item">
<label class="layui-form-label">分类名称</label>
<div class="layui-input-block">
<input type="text" name="name" value='{$vo.name|default=""}' required placeholder="请输入分类名称" class="layui-input">
</div>
</div>
</div>
<div class="hr-line-dashed"></div>
{notempty name='vo.id'}<input type='hidden' value='{$vo.id}' name='id'>{/notempty}
<div class="layui-form-item text-center">
<button class="layui-btn layui-btn-normal" type='submit'>保存数据</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-close>取消编辑</button>
</div>
</div>
</form>
<style>
.layui-tab-item{ min-height: 400px;}
.input-group-addon {
top: 0;
right: 0;
color: #eee;
width: 25px;
padding: 7px;
position: absolute;
margin-top: -1px;
text-align: center;
background: #393D49;
}
</style>
<script>
layui.form.render();
layui.use(['layer', 'jquery', 'form','element'], function () {
var layer = layui.layer,
$ = layui.jquery,
form = layui.form,
element = layui.element;
//监听是否为跳转链接的选择变化
form.on("radio(islink_filter)", function (data) {
if(parseInt(data.value)){
$('#category_link_url').show();
}else{
$('#category_link_url').hide();
}
});
//监听指定开关
form.on('switch(urlpath_filter)', function(data){
if(this.checked){
$(this).parent('.layui-input-block').prev().removeClass('label-required-next');
$('input[name="url_path"]').next('span').hide();
$('input[name="url_path"]').removeAttr('required').attr('disabled',true);
}else{
$(this).parent('.layui-input-block').prev().addClass('label-required-next');
$('input[name="url_path"]').next('span').show();
$('input[name="url_path"]').removeAttr('disabled').attr('required',true);
}
});
});
</script>

View File

@ -0,0 +1,83 @@
{extend name="table"}
{block name="button"}
<!--{if auth("add")}-->
<button data-modal='{:url("add")}' data-title="添加栏目" class='layui-btn layui-btn-sm layui-btn-primary'>添加类型</button>
<!--{/if}-->
{/block}
{block name="content"}
<div class="layui-tab layui-tab-card think-bg-white">
<div class="layui-tab-content think-box-shadow">
{include file='type/index_search'}
<table class="layui-table margin-top-10" data-url="{$request->url()}" data-target-search="form.form-search" lay-skin="line" id="typeTable"></table>
</div>
</div>
<script type="text/html" id="toolbar">
<!--{if auth("edit")}-->
<a data-modal="{:url('edit')}?id={{ d.id }}" class="layui-btn layui-btn-xs layui-btn-normal" title="修改">修改</a>
<!--{/if}-->
<!--{if auth("status")}-->
{{# if(d.status == 0){ }}
<a class="layui-btn layui-btn-xs layui-btn-normal" data-title="显示分类" data-modal='{:url("status")}?id={{ d.id }}&status=1'> </a>
{{# } else { }}
<a class="layui-btn layui-btn-xs layui-btn-primary" data-title="隐藏分类" data-modal='{:url("status")}?id={{ d.id }}&status=0'> </a>
{{# } }}
<!--{/if}-->
<!--{if auth("remove")}-->
<a data-confirm="确定要永久删除此分类吗?" data-action="{:url('remove')}" data-value="id#{{ d.id }}" data-csrf="{:systoken('remove')}" class="layui-btn layui-btn-xs layui-btn-danger" title="删除">删除</a>
<!--{/if}-->
</script>
{/block}
{block name='style'}
<style>
</style>
{/block}
{block name='script'}
<script>
$(function () {
$('#typeTable').layTable({
height: 'full-240',
toolbar: true,
sort: {field: 'sort', type: 'asc'},
cols: [[
{type: 'numbers'},
{field: 'sort', title: '排序', width: 80, sort: true, edit: 'text'},
{field: 'name', title: '工单类型名称', minWidth: 165},
{field: 'status', title: '状态', width: 60,templet:function(item){
if(item.status == 0){
return '<span style="color:red;">隐藏</span>';
}else if(item.status == 1){
return '<span style="color:green;">显示</span>';
}
}},
{field: 'tickets_count', title: '该类型工单数', width: 120},
{field: 'create_at', title: '创建时间', width: 150},
{align: 'center', toolbar: '#toolbar', title: '操作', width: 240}
]]
})
layui.use(['table'], function(){
var table = layui.table,
layer = layui.layer;
table.on('edit(typeTable)', function(obj) {
if (obj.field == 'sort') {
if (obj.value == '') {
obj.value = obj.oldValue
return false;
} else {
$.form.load("{:url('sort')}", {sort: obj.value, id: obj.data.id}, 'post', function (ret) {
$('#typeTable').trigger('reload');
});
}
}
});
})
})
</script>
{/block}

View File

@ -0,0 +1,16 @@
<fieldset>
<legend>条件搜索</legend>
<form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
<div class="layui-form-item layui-inline">
<label class="layui-form-label">分类名称</label>
<div class="layui-input-inline">
<input name="name" value="{:input('get.name')}" placeholder="请输入分类名称" class="layui-input">
</div>
</div>
<div class="layui-form-item layui-inline">
<button class="layui-btn layui-btn-primary" lay-submit lay-filter="searchData"><i class="layui-icon">&#xe615;</i> 搜 索</button>
</div>
</form>
</fieldset>