fastadmin 实现标签的多选研究---基于fa的test案例,已经CMS中的标签写法
官方文档
动态下拉组件
https://doc.fastadmin.net/doc/178.html
首先生成控制器和菜单看下fa_test的多选效果
多选效果如下
使用多选的两种方式(chekbox是最简单的,那中多选就不算了)
1.input里使用多选(官网中,以分类id的多选为例,因为ui的逼格比较高,所以我使用这个。)
view
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Category_ids')}:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-category_ids" data-rule="required" data-source="category/selectpage" data-params='{"custom[type]":"test"}' data-multiple="true" class="form-control selectpage" name="row[category_ids]" type="text" value="">
</div>
</div>
2.select里使用多选(官网中,以状态的多选为例子)
view
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('Flag')}:</label>
<div class="col-xs-12 col-sm-8">
<select id="c-flag" data-rule="required" class="form-control selectpicker" multiple="" name="row[flag][]">
{foreach name="flagList" item="vo"}
<option value="{$key}" {in name="key" value=""}selected{/in}>{$vo}</option>
{/foreach}
</select>
</div>
</div>
具体的方式,大家,看相关的test控制器和js,以及模型
控制器中直接引入一个方法
/**
* Selectpage搜索
*
* @internal
*/
public function selectpage()
{
return parent::selectpage();
}
}
如何,让多选项是树状效果呢?
注释掉父类的方法,重写自己的方法,最后不需要重写
直接在input里增加一个参数
1.data-params='{"isTree":1}'
同时,可以使用
2.data-params='{"custom[type]":"test"}',来限制返回的字段
/**
* Selectpage搜索
*
* @internal
*/
public function selectpage()
{
// return parent::selectpage();
// 注释掉继承的,下面是重写的
// 获取标签列表,渲染成右侧的树形,记得引入use fast/tree 这个命名空间
$wenzhang_tags_right=collection(db::name("cms_exams_tags")->select())->toArray();
$tree = Tree::instance();
$tree->init($wenzhang_tags_right, 'parent');
$this->tagsListRight = $tree->getTreeList($tree->getTreeArray(0), 'text');
//对请求进行判断,并返回json数据,
if ($this->request->isAjax()) {
//构造父类select列表选项数据
$list = $this->tagsListRight;
$list = array_values($list);
// 下面这个,是设置pid的,不设置,就会成undefine,前端的展开隐藏,靠这个pid
foreach ($list as $k => &$v) {
$v['pid'] = $v['parent'];
}
$total = count($list);
$result = array("total" => $total, "rows" => $list);
return json($result);
}
}
接下来,就是多个tag如何存储的问题,以及如何读取的问题了
1.存储,要操作tag表
参考官方的tags.php控制器
并研究官方的selectpage函数代码
protected function selectpage()
{
//设置过滤方法
$this->request->filter(['strip_tags', 'htmlspecialchars']);
//搜索关键词,客户端输入以空格分开,这里接收为数组
$word = (array)$this->request->request("q_word/a");
//当前页
$page = $this->request->request("pageNumber");
//分页大小
$pagesize = $this->request->request("pageSize");
//搜索条件
$andor = $this->request->request("andOr", "and", "strtoupper");
//排序方式
$orderby = (array)$this->request->request("orderBy/a");
//显示的字段
$field = $this->request->request("showField");
//主键
$primarykey = $this->request->request("keyField");
//主键值
$primaryvalue = $this->request->request("keyValue");
//搜索字段
$searchfield = (array)$this->request->request("searchField/a");
//自定义搜索条件
$custom = (array)$this->request->request("custom/a");
//是否返回树形结构
$istree = $this->request->request("isTree", 0);
$ishtml = $this->request->request("isHtml", 0);
if ($istree) {
$word = [];
$pagesize = 99999;
}
$order = [];
foreach ($orderby as $k => $v) {
$order[$v[0]] = $v[1];
}
$field = $field ? $field : 'name';
//如果有primaryvalue,说明当前是初始化传值
if ($primaryvalue !== null) {
$where = [$primarykey => ['in', $primaryvalue]];
$pagesize = 99999;
} else {
$where = function ($query) use ($word, $andor, $field, $searchfield, $custom) {
$logic = $andor == 'AND' ? '&' : '|';
$searchfield = is_array($searchfield) ? implode($logic, $searchfield) : $searchfield;
foreach ($word as $k => $v) {
$query->where(str_replace(',', $logic, $searchfield), "like", "%{$v}%");
}
if ($custom && is_array($custom)) {
foreach ($custom as $k => $v) {
if (is_array($v) && 2 == count($v)) {
$query->where($k, trim($v[0]), $v[1]);
} else {
$query->where($k, '=', $v);
}
}
}
};
}
$adminIds = $this->getDataLimitAdminIds();
if (is_array($adminIds)) {
$this->model->where($this->dataLimitField, 'in', $adminIds);
}
$list = [];
$total = $this->model->where($where)->count();
if ($total > 0) {
if (is_array($adminIds)) {
$this->model->where($this->dataLimitField, 'in', $adminIds);
}
$datalist = $this->model->where($where)
->order($order)
->page($page, $pagesize)
->field($this->selectpageFields)
->select();
foreach ($datalist as $index => $item) {
unset($item['password'], $item['salt']);
$list[] = [
$primarykey => isset($item[$primarykey]) ? $item[$primarykey] : '',
$field => isset($item[$field]) ? $item[$field] : '',
'pid' => isset($item['pid']) ? $item['pid'] : 0
];
}
if ($istree && !$primaryvalue) {
$tree = Tree::instance();
$tree->init(collection($list)->toArray(), 'pid');
$list = $tree->getTreeList($tree->getTreeArray(0), $field);
if (!$ishtml) {
foreach ($list as &$item) {
$item = str_replace(' ', ' ', $item);
}
unset($item);
}
}
}
//这里一定要返回有list这个字段,total是可选的,如果total<=list的数量,则会隐藏分页按钮
return json(['list' => $list, 'total' => $total]);
}
archives模型init中的更新tags的代码
self::afterWrite(function ($row) use ($config) {
if (isset($row['channel_id'])) {
//在更新成功后刷新副表、TAGS表数据、栏目表
$channel = Channel::get($row->channel_id);
if ($channel) {
$model = Modelx::get($channel['model_id']);
if ($model && isset($row['content'])) {
$values = array_intersect_key($row->getData(), array_flip($model->fields));
$values['id'] = $row['id'];
$values['content'] = $row['content'];
db($model['table'])->insert($values, true);
}
}
}
if (isset($row['tags'])) {
$tags = array_filter(explode(',', $row['tags']));
if ($tags) {
$tagslist = Tags::where('name', 'in', $tags)->select();
foreach ($tagslist as $k => $v) {
$archives = explode(',', $v['archives']);
if (!in_array($row['id'], $archives)) {
$archives[] = $row['id'];
$v->archives = implode(',', $archives);
$v->nums++;
$v->save();
}
$tags = array_udiff($tags, [$v['name']], 'strcasecmp');
}
$list = [];
foreach ($tags as $k => $v) {
$list[] = ['name' => $v, 'archives' => $row['id'], 'nums' => 1];
}
if ($list) {
(new Tags())->saveAll($list);
}
}
}
$changedData = $row->getChangedData();
if (isset($changedData['status']) && $changedData['status'] == 'normal') {
//增加积分
User::score($config['score']['postarchives'], $row['user_id'], '发布文章');
//推送到熊掌号和百度站长
if ($config['baidupush']) {
$urls = [$row->fullurl];
\think\Hook::listen("baidupush", $urls);
}
}
if ($config['searchtype'] == 'xunsearch') {
//更新全文搜索
FulltextSearch::update($row->id);
}
});
这里我抄袭tags表的更新方法,修改自己的examstags表的tag数据
// 新增tag分组,不能新增,只能选择,所以,不需要对比,直接插入新的tags
if (isset($row['tags_ids'])) {
$mytags = array_filter(explode(',', $row['tags_ids']));
if ($mytags) {
// 获取数据表里的tags数据
$mytagslist = Examstags::where('id', 'in', $mytags)->select();
foreach ($mytagslist as $k => $v) {
$archives = explode(',', $v['archive_id']);
// 如果当前的id,不在数据表的archives,那么就新增一下
if (!in_array($row['id'], $archives)) {
$archives[] = $row['id'];
$v->archive_id = implode(',', $archives);
// 忘记设置nums了,这里也去掉
// $v->nums++;
$v->save();
}
// 再看看,有没有新增标签,因为我们展示不支持新增,所以,暂时不用这个
// $tags = array_udiff($tags, [$v['name']], 'strcasecmp');
}
// $list = [];
// foreach ($tags as $k => $v) {
// $list[] = ['name' => $v, 'archives' => $row['id'], 'nums' => 1];
// }
// if ($list) {
// (new Tags())->saveAll($list);
// }
}
}
2.读取,也要操作tag表,并且,view视图里,如何写,也是个问题
//在新增页面,新增树状结构的tag下拉选项
<div class="form-group">
<label class="control-label col-xs-12 col-sm-3">{:__('标签(多选)')}:</label>
<div class="col-xs-12 col-sm-8">
//默认的字段为name,可以显示出中文,如果字段不是name,那么使用data-field="xxx",来显示tag的中文
<input id="c-tags_ids" data-rule="required" data-source="cms/examstags/selectpage" data-field="text" data-multiple="true" class="form-control selectpage" name="row[tags_ids]" type="text" value="">
</div>
</div>
//在编辑页面获取当前的,多选的tag
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">{:__('tags_ids')}:</label>
<div class="col-xs-12 col-sm-8">
//默认的字段为name,可以显示出中文,如果字段不是name,那么使用data-field="xxx",来显示tag的中文
<input id="c-tags_ids" data-rule="required" data-source="cms/examstags/selectpage" data-field="text" data-multiple="true" class="form-control selectpage" name="row[tags_ids]" type="text" value="{$row.tags_ids|htmlentities}">
</div>
</div>
默认情况下,是显示所有的tag,id。但是编辑页面,要显示当前的tags,id
对主键值进行判断。为什么要进行这样的判断呢?
if ($istree&& !$primaryvalue) {
$list = $this->tagsListRight;
$list = array_values($list);
// 下面这个,是设置pid的,不设置,就会成undefine,前端的展开隐藏,靠这个pid
foreach ($list as $k => &$v) {
$v['pid'] = $v['parent'];
}
if (!$ishtml) {
foreach ($list as &$item) {
$item = str_replace(' ', ' ', $item);
}
unset($item);
}
}
3.如何搜索
4.如何批量录入