feat: 公共表更名 common/member 前缀 + 插件命令自动加载 + 版本 1.1.1
- 18 张公共表更名(addon/attachment/configure/links/spider_log/spider_stat/sms/notice/ad/task/prop/medal/help/card -> common_*,member_wallets->member_wallet,score_rule/score_log -> member_*,addon_config->common_addonconf),模型全部对齐新表名,Db 直引用清零 - Attachment 模型补 表名绑定,修复富文本上传查 wxapp_attachment 1146 隐患 - install.sql + 迁移 SQL:backend_admin/backend_role delete_at 默认 0,修复软删除(NULL != 0)误过滤导致后台菜单为空 - AppService::boot() 支持插件 info.php 声明 commands 自动注册插件命令(psr-4 自动加载,坏类名自动跳过) - 各插件(haonav/mqttbroker/wxchat/articles/blog/forum 等)字段与配置同步调整 - 框架版本 1.1.0 -> 1.1.1
This commit is contained in:
@@ -13,7 +13,7 @@ namespace addon\haonav;
|
||||
|
||||
use think\facade\Config;
|
||||
use think\facade\Db;
|
||||
use ywxapp\Addons;
|
||||
use ywxapp\AddonBase;
|
||||
use ywxapp\model\BaseModel;
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ use ywxapp\model\BaseModel;
|
||||
* favorites / favorite_shares / ads / apply / click_stats);
|
||||
* 卸载时按统一前缀清理全部表;升级时经 callAddonHook('upgrade') 收敛表结构。
|
||||
*/
|
||||
class Addon extends Addons
|
||||
class Addon extends AddonBase
|
||||
{
|
||||
/**
|
||||
* 安装钩子(框架自动导入 install.sql 后调用)。
|
||||
|
||||
@@ -93,10 +93,17 @@ class Index extends FrontendBase
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$data = CategoryModel::with(['links' =>
|
||||
function ($query) {
|
||||
$query->where('status', 1)->limit(10)->order('click_count', 'desc');
|
||||
}])->where('status', 1)->order('sort', 'desc')->select();
|
||||
$data = CategoryModel::with([
|
||||
'children' => function ($query) {
|
||||
$query->with(['subLinks' => function ($q) {
|
||||
$q->limit(12)->order('click_count', 'desc');
|
||||
}]);
|
||||
},
|
||||
'links' => function ($query) {
|
||||
// 首页不按二级分组,直接展示该一级分类下的全部链接(含已归属二级的)
|
||||
$query->where('status', 1)->order('click_count', 'desc')->limit(30);
|
||||
},
|
||||
])->where('pid', 0)->where('status', 1)->order('sort', 'desc')->select();
|
||||
|
||||
$hotspot = LinksModel::where('is_hot', 1)->where('status', 1)->limit(18)->order('click_count', 'desc')->select();
|
||||
$links = LinksModel::where('is_recommend', 1)->where('status', 1)->limit(10)->order('sort', 'desc')->select();
|
||||
@@ -441,11 +448,39 @@ class Index extends FrontendBase
|
||||
*/
|
||||
public function category($id = 0)
|
||||
{
|
||||
$data = CategoryModel::with(['links' =>
|
||||
function ($query) {
|
||||
$query->where('status', 1)->order('click_count', 'desc');
|
||||
}])->find($id);
|
||||
$this->view->assign('info', $data);
|
||||
$info = CategoryModel::with([
|
||||
'children' => function ($query) {
|
||||
$query->with(['subLinks' => function ($q) {
|
||||
$q->order('click_count', 'desc');
|
||||
}]);
|
||||
},
|
||||
])->where('status', 1)->find($id);
|
||||
|
||||
if (!$info) {
|
||||
return redirect('/haonav/index.html');
|
||||
}
|
||||
|
||||
// 顶级分类且有子分类:按二级子分类分组展示
|
||||
if ($info->pid == 0 && $info->children && $info->children->count() > 0) {
|
||||
// 未细分二级的链接(sub_cid=0)归入「常用」分组,排在最后
|
||||
$others = LinksModel::where('cid', $id)
|
||||
->where('sub_cid', 0)
|
||||
->where('status', 1)
|
||||
->order('click_count', 'desc')
|
||||
->select();
|
||||
$this->view->assign('others', $others);
|
||||
$this->view->assign('mode', 'group');
|
||||
} else {
|
||||
// 二级分类 / 叶子分类:直接列出该分类下的全部链接
|
||||
$links = ($info->pid == 0)
|
||||
? LinksModel::where('cid', $id)->where('status', 1)
|
||||
: LinksModel::where('sub_cid', $id)->where('status', 1);
|
||||
$links = $links->order('click_count', 'desc')->select();
|
||||
$this->view->assign('links', $links);
|
||||
$this->view->assign('mode', 'flat');
|
||||
}
|
||||
|
||||
$this->view->assign('info', $info);
|
||||
$this->assignSeo();
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
@@ -57,10 +57,11 @@ class Category extends HaonavBackend
|
||||
$page = $this->request->param('page/d', 1);
|
||||
$limit = $this->request->param('limit/d', 20);
|
||||
$data = $this->model->withAttr('cate')
|
||||
->when($title, fn($q, $t) => $q->whereLike('title', "%{$t}%"))
|
||||
->paginate(['page' => $page, 'list_rows' => $limit]);
|
||||
$this->result->setCount($data->total());
|
||||
$this->result->success($data->items());
|
||||
->when($title, fn($q, $t) => $q->whereLike('title', "%{$t}%"))->select();
|
||||
// ->paginate(['page' => $page, 'list_rows' => $limit]);
|
||||
// $this->result->setCount($data->total());
|
||||
// $this->result->success($data->items());
|
||||
$this->result->success($data);
|
||||
}
|
||||
return $this->view->fetch('category/index');
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ return [
|
||||
'intro' => '简洁实用的网址导航,支持分类管理、链接收录、热门/推荐与点击统计。',
|
||||
'author' => 'ywxapp',
|
||||
'website' => 'https://github.com',
|
||||
'version' => '1.0.9',
|
||||
'version' => '1.0.10',
|
||||
'state' => 1,
|
||||
'url' => '/haonav',
|
||||
'license' => '',
|
||||
@@ -34,6 +34,6 @@ return [
|
||||
],
|
||||
'services' => [
|
||||
],
|
||||
'install_time' => 1785818546,
|
||||
'install_time' => 1786890830,
|
||||
'update_time' => 1786365870,
|
||||
];
|
||||
+3035
-4
File diff suppressed because it is too large
Load Diff
@@ -41,6 +41,24 @@ class Category extends BaseModel
|
||||
return $this->hasMany(Links::class, 'cid', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 二级分类(pid 指向自身)
|
||||
*/
|
||||
public function children()
|
||||
{
|
||||
return $this->hasMany(self::class, 'pid', 'id')->where('status', 1)->order('sort', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* 按二级分类分组的链接(首页/分类页用)
|
||||
* 返回二级分类及其下属链接,sub_cid=0 的链接归入「常用」
|
||||
*/
|
||||
public function subLinks()
|
||||
{
|
||||
return $this->hasMany(Links::class, 'sub_cid', 'id')
|
||||
->where('status', 1)->order('click_count', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除前保护:存在子分类或下属链接时禁止删除,避免产生孤儿数据
|
||||
* @param \think\Model $model
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @Author: YwxApp <ywx@ywxapp.cn>
|
||||
* @Date: 2026-07-31 13:28:15
|
||||
* @LastEditors: YwxApp <ywx@ywxapp.cn>
|
||||
* @LastEditTime: 2026-08-10 23:40:51
|
||||
* @LastEditTime: 2026-08-20 12:27:18
|
||||
* @Description:
|
||||
* @FilePath: \ywxapp_dev\addon\haonav\route\app.php
|
||||
* @CustomString: Copyright (c) 2026 YwxApp
|
||||
@@ -84,6 +84,7 @@ Route::group('backend', function () {
|
||||
Route::rule('links/:action', 'Links/:action');
|
||||
Route::rule('category', 'Category/index') ;
|
||||
Route::rule('category/index', 'Category/index') ;
|
||||
Route::rule('category/update', 'Category/update') ;
|
||||
Route::rule('configs', 'Configs/index');
|
||||
Route::rule('configs/:action', 'Configs/:action');
|
||||
Route::rule('ad', 'Ad/index');
|
||||
|
||||
@@ -16,3 +16,83 @@ INSERT IGNORE INTO `__PREFIX__haonav_config` (`id`, `name`, `group`, `title`, `t
|
||||
(2, 'enable_submit', 'site', '开启网址投稿', '关闭后前台投稿将直接上架,无需审核', 'bool', '1', '', '', '', ''),
|
||||
(3, 'enable_hot_api', 'site', '启用外部热榜', '开启后将尝试聚合外部热搜(可能较慢,失败自动降级)', 'bool', '0', '', '', '', ''),
|
||||
(4, 'check_token', 'task', '死链检测令牌', 'cron 调用 /haonav/task/checklinks?token= 时校验,留空则拒绝执行', 'string', '', '', '', '', '');
|
||||
|
||||
-- 3. 链接表新增二级分类字段
|
||||
ALTER TABLE `__PREFIX__haonav_links`
|
||||
ADD COLUMN `sub_cid` int unsigned NOT NULL DEFAULT '0' COMMENT '所属二级分类ID,0表示未细分' AFTER `cid`;
|
||||
ALTER TABLE `__PREFIX__haonav_links`
|
||||
ADD INDEX `idx_sub_cid` (`sub_cid`);
|
||||
|
||||
-- 4. 写入二级分类(pid 指向一级,已存在则忽略)
|
||||
INSERT IGNORE INTO `__PREFIX__haonav_category` (`id`, `pid`, `title`, `icon`, `summary`, `keywords`, `description`, `sort`, `status`, `website_count`, `click_count`, `create_at`, `update_at`) VALUES
|
||||
(101, 1, '综合社交', '💬', '', '', '微博、朋友圈等综合社交平台', 100, 1, 0, 0, NULL, NULL),
|
||||
(102, 1, '即时通讯', '📱', '', '', '微信、QQ、Telegram 等通讯工具', 90, 1, 0, 0, NULL, NULL),
|
||||
(103, 1, '兴趣社区', '🏘️', '', '', '豆瓣、贴吧、知乎等兴趣社区', 80, 1, 0, 0, NULL, NULL),
|
||||
(104, 1, '职场交友', '🤝', '', '', '脉脉、探探、Soul 等职场与交友', 70, 1, 0, 0, NULL, NULL),
|
||||
(105, 2, '长视频', '🎬', '', '', '腾讯视频、爱奇艺、优酷等长视频', 100, 1, 0, 0, NULL, NULL),
|
||||
(106, 2, '短视频', '📱', '', '', '抖音、快手、B站等短视频', 90, 1, 0, 0, NULL, NULL),
|
||||
(107, 2, '动漫漫画', '🎨', '', '', '哔哩漫画、腾讯动漫、快看', 80, 1, 0, 0, NULL, NULL),
|
||||
(108, 2, '直播娱乐', '🎥', '', '', '直播平台与综合娱乐', 70, 1, 0, 0, NULL, NULL),
|
||||
(109, 3, '综合电商', '🛍️', '', '', '淘宝、京东、拼多多等综合电商', 100, 1, 0, 0, NULL, NULL),
|
||||
(110, 3, '特卖二手', '♻️', '', '', '唯品会、闲鱼、转转等特卖与二手', 90, 1, 0, 0, NULL, NULL),
|
||||
(111, 3, '本地生活', '🍜', '', '', '美团、大众点评、旅游出行', 80, 1, 0, 0, NULL, NULL),
|
||||
(112, 3, '汽车房产', '🚗', '', '', '汽车之家、链家、贝壳等', 70, 1, 0, 0, NULL, NULL),
|
||||
(113, 5, '在线课程', '📝', '', '', 'MOOC、网易云课堂、极客时间', 100, 1, 0, 0, NULL, NULL),
|
||||
(114, 5, '学术检索', '🔬', '', '', '知网、万方、Google Scholar', 90, 1, 0, 0, NULL, NULL),
|
||||
(115, 5, '基础教育', '📐', '', '', '可汗学院、学堂在线等', 80, 1, 0, 0, NULL, NULL),
|
||||
(116, 6, '综合新闻', '📰', '', '', '人民日报、新华社、央视网', 100, 1, 0, 0, NULL, NULL),
|
||||
(117, 6, '财经科技', '💡', '', '', '财新、36氪、界面新闻', 90, 1, 0, 0, NULL, NULL),
|
||||
(118, 6, '健康医疗', '🩺', '', '', '丁香医生、好大夫在线', 80, 1, 0, 0, NULL, NULL),
|
||||
(119, 7, '游戏平台', '🕹️', '', '', 'Steam、TapTap、腾讯游戏', 100, 1, 0, 0, NULL, NULL),
|
||||
(120, 7, '小游戏', '🎲', '', '', '4399、7k7k 等小游戏', 90, 1, 0, 0, NULL, NULL),
|
||||
(121, 8, '搜索引擎', '🔍', '', '', '百度、Google、Bing 等', 100, 1, 0, 0, NULL, NULL),
|
||||
(122, 8, '门户邮箱', '📧', '', '', '新浪、搜狐、QQ邮箱、Gmail', 90, 1, 0, 0, NULL, NULL),
|
||||
(123, 8, '翻译地图', '🗺️', '', '', '翻译、地图等工具门户', 80, 1, 0, 0, NULL, NULL),
|
||||
(124, 9, '代码托管', '📦', '', '', 'GitHub、GitLab、Gitee', 100, 1, 0, 0, NULL, NULL),
|
||||
(125, 9, '技术社区', '👨💻', '', '', 'CSDN、掘金、Stack Overflow', 90, 1, 0, 0, NULL, NULL),
|
||||
(126, 9, '云服务', '☁️', '', '', '阿里云、腾讯云、Cloudflare', 80, 1, 0, 0, NULL, NULL),
|
||||
(127, 9, '设计素材', '🎨', '', '', '站酷、花瓣、摄图网', 70, 1, 0, 0, NULL, NULL),
|
||||
(128, 10, '网络文学', '📖', '', '', '起点、晋江、纵横等', 100, 1, 0, 0, NULL, NULL),
|
||||
(129, 10, '电子阅读', '📚', '', '', '微信读书、掌阅、番茄小说', 90, 1, 0, 0, NULL, NULL),
|
||||
(130, 11, '办公套件', '📊', '', '', 'WPS、Office 等办公套件', 100, 1, 0, 0, NULL, NULL),
|
||||
(131, 11, '企业协同', '👥', '', '', '钉钉、飞书、企业微信', 90, 1, 0, 0, NULL, NULL),
|
||||
(132, 11, '云存储', '💾', '', '', '百度网盘、阿里云盘', 80, 1, 0, 0, NULL, NULL),
|
||||
(133, 11, '音乐音频', '🎵', '', '', '网易云、QQ音乐、Spotify', 70, 1, 0, 0, NULL, NULL),
|
||||
(134, 12, '证券理财', '📈', '', '', '东方财富、同花顺、雪球', 100, 1, 0, 0, NULL, NULL),
|
||||
(135, 12, '支付银行', '🏦', '', '', '支付宝、招商银行等', 90, 1, 0, 0, NULL, NULL);
|
||||
|
||||
-- 5. 已装库旧数据按 URL 特征归入二级(仅更新 sub_cid=0 的记录,安全可重复执行)
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 102 WHERE `sub_cid` = 0 AND (`url` LIKE '%weixin.qq%' OR `url` LIKE '%im.qq%' OR `url` LIKE '%telegram%' OR `url` LIKE '%whatsapp%' OR `url` LIKE '%skype%' OR `url` LIKE '%discord%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 103 WHERE `sub_cid` = 0 AND (`url` LIKE '%douban%' OR `url` LIKE '%tieba%' OR `url` LIKE '%zhihu%' OR `url` LIKE '%weibo%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 104 WHERE `sub_cid` = 0 AND (`url` LIKE '%maimai%' OR `url` LIKE '%tantan%' OR `url` LIKE '%soulapp%' OR `url` LIKE '%immomo%' OR `url` LIKE '%jiayuan%' OR `url` LIKE '%yidui%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 105 WHERE `sub_cid` = 0 AND (`url` LIKE '%v.qq%' OR `url` LIKE '%iqiyi%' OR `url` LIKE '%youku%' OR `url` LIKE '%mgtv%' OR `url` LIKE '%sohu.com%tv%' OR `url` LIKE '%miguvideo%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 106 WHERE `sub_cid` = 0 AND (`url` LIKE '%douyin%' OR `url` LIKE '%kuaishou%' OR `url` LIKE '%bilibili%' OR `url` LIKE '%ixigua%' OR `url` LIKE '%haokan%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 107 WHERE `sub_cid` = 0 AND (`url` LIKE '%manga%' OR `url` LIKE '%manhua%' OR `url` LIKE '%ac.qq%' OR `url` LIKE '%u17%' OR `url` LIKE '%dmzj%' OR `url` LIKE '%manhuadb%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 108 WHERE `sub_cid` = 0 AND (`url` LIKE '%douyu%' OR `url` LIKE '%huya%' OR `url` LIKE '%live%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 109 WHERE `sub_cid` = 0 AND (`url` LIKE '%taobao%' OR `url` LIKE '%tmall%' OR `url` LIKE '%jd.com%' OR `url` LIKE '%pinduoduo%' OR `url` LIKE '%suning%' OR `url` LIKE '%amazon%' OR `url` LIKE '%mi.com%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 110 WHERE `sub_cid` = 0 AND (`url` LIKE '%vip.com%' OR `url` LIKE '%dangdang%' OR `url` LIKE '%goofish%' OR `url` LIKE '%zhuanzhuan%' OR `url` LIKE '%xianyu%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 111 WHERE `sub_cid` = 0 AND (`url` LIKE '%meituan%' OR `url` LIKE '%ele.me%' OR `url` LIKE '%dianping%' OR `url` LIKE '%ctrip%' OR `url` LIKE '%fliggy%' OR `url` LIKE '%qunar%' OR `url` LIKE '%mafengwo%' OR `url` LIKE '%12306%' OR `url` LIKE '%ly.com%' OR `url` LIKE '%kfc%' OR `url` LIKE '%mcdonalds%' OR `url` LIKE '%xiachufang%' OR `url` LIKE '%meishij%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 112 WHERE `sub_cid` = 0 AND (`url` LIKE '%autohome%' OR `url` LIKE '%dongchedi%' OR `url` LIKE '%yiche%' OR `url` LIKE '%lianjia%' OR `url` LIKE '%ke.com%' OR `url` LIKE '%anjuke%' OR `url` LIKE '%5i5j%' OR `url` LIKE '%guazi%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 113 WHERE `sub_cid` = 0 AND (`url` LIKE '%icourse%' OR `url` LIKE '%study.163%' OR `url` LIKE '%ke.qq%' OR `url` LIKE '%xuetangx%' OR `url` LIKE '%imooc%' OR `url` LIKE '%geekbang%' OR `url` LIKE '%jikexueyuan%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 114 WHERE `sub_cid` = 0 AND (`url` LIKE '%cnki%' OR `url` LIKE '%wanfang%' OR `url` LIKE '%cqvip%' OR `url` LIKE '%scholar.google%' OR `url` LIKE '%xueshu.baidu%' OR `url` LIKE '%smartedu%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 115 WHERE `sub_cid` = 0 AND (`url` LIKE '%khanacademy%' OR `url` LIKE '%coursera%' OR `url` LIKE '%edx%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 116 WHERE `sub_cid` = 0 AND (`url` LIKE '%people.com%' OR `url` LIKE '%news.cn%' OR `url` LIKE '%cctv%' OR `url` LIKE '%chinanews%' OR `url` LIKE '%huanqiu%' OR `url` LIKE '%cankaoxiaoxi%' OR `url` LIKE '%thepaper%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 117 WHERE `sub_cid` = 0 AND (`url` LIKE '%caixin%' OR `url` LIKE '%jiemian%' OR `url` LIKE '%36kr%' OR `url` LIKE '%tmtpost%' OR `url` LIKE '%leiphone%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 118 WHERE `sub_cid` = 0 AND (`url` LIKE '%dxy.com%' OR `url` LIKE '%haodf%' OR `url` LIKE '%jk.cn%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 119 WHERE `sub_cid` = 0 AND (`url` LIKE '%steampowered%' OR `url` LIKE '%taptap%' OR `url` LIKE '%game.qq%' OR `url` LIKE '%game.163%' OR `url` LIKE '%epicgames%' OR `url` LIKE '%wegame%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 120 WHERE `sub_cid` = 0 AND (`url` LIKE '%4399%' OR `url` LIKE '%7k7k%' OR `url` LIKE '%2144%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 121 WHERE `sub_cid` = 0 AND (`url` LIKE '%baidu.com%' OR `url` LIKE '%google.com%' OR `url` LIKE '%bing%' OR `url` LIKE '%sogou%' OR `url` LIKE '%yahoo%' OR `url` LIKE '%duckduckgo%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 122 WHERE `sub_cid` = 0 AND (`url` LIKE '%sina%' OR `url` LIKE '%sohu%' OR `url` LIKE '%mail.qq%' OR `url` LIKE '%mail.163%' OR `url` LIKE '%outlook%' OR `url` LIKE '%gmail%' OR `url` LIKE '%foxmail%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 123 WHERE `sub_cid` = 0 AND (`url` LIKE '%fanyi%' OR `url` LIKE '%map.baidu%' OR `url` LIKE '%amap%' OR `url` LIKE '%translate%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 124 WHERE `sub_cid` = 0 AND (`url` LIKE '%github%' OR `url` LIKE '%gitlab%' OR `url` LIKE '%gitee%' OR `url` LIKE '%bitbucket%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 125 WHERE `sub_cid` = 0 AND (`url` LIKE '%csdn%' OR `url` LIKE '%juejin%' OR `url` LIKE '%stackoverflow%' OR `url` LIKE '%oschina%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 126 WHERE `sub_cid` = 0 AND (`url` LIKE '%aliyun%' OR `url` LIKE '%cloud.tencent%' OR `url` LIKE '%huaweicloud%' OR `url` LIKE '%qiniu%' OR `url` LIKE '%cloudflare%' OR `url` LIKE '%cloud.baidu%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 127 WHERE `sub_cid` = 0 AND (`url` LIKE '%zcool%' OR `url` LIKE '%huaban%' OR `url` LIKE '%ui.cn%' OR `url` LIKE '%699pic%' OR `url` LIKE '%58pic%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 128 WHERE `sub_cid` = 0 AND (`url` LIKE '%qidian%' OR `url` LIKE '%jjwxc%' OR `url` LIKE '%zongheng%' OR `url` LIKE '%hongxiu%' OR `url` LIKE '%xxsy%' OR `url` LIKE '%heiyan%' OR `url` LIKE '%fanqie%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 129 WHERE `sub_cid` = 0 AND (`url` LIKE '%weread%' OR `url` LIKE '%zhangyue%' OR `url` LIKE '%read.douban%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 130 WHERE `sub_cid` = 0 AND (`url` LIKE '%wps%' OR `url` LIKE '%office%' OR `url` LIKE '%yozosoft%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 131 WHERE `sub_cid` = 0 AND (`url` LIKE '%dingtalk%' OR `url` LIKE '%feishu%' OR `url` LIKE '%work.weixin%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 132 WHERE `sub_cid` = 0 AND (`url` LIKE '%pan.baidu%' OR `url` LIKE '%aliyundrive%' OR `url` LIKE '%lanzou%' OR `url` LIKE '%cloud.189%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 133 WHERE `sub_cid` = 0 AND (`url` LIKE '%music.163%' OR `url` LIKE '%y.qq%' OR `url` LIKE '%kugou%' OR `url` LIKE '%kuwo%' OR `url` LIKE '%spotify%' OR `url` LIKE '%music.apple%' OR `url` LIKE '%ximalaya%' OR `url` LIKE '%qtfm%' OR `url` LIKE '%lizhi%' OR `url` LIKE '%kg.qq%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 134 WHERE `sub_cid` = 0 AND (`url` LIKE '%eastmoney%' OR `url` LIKE '%10jqka%' OR `url` LIKE '%xueqiu%' OR `url` LIKE '%finance.sina%' OR `url` LIKE '%hexun%' OR `url` LIKE '%dzh%');
|
||||
UPDATE `__PREFIX__haonav_links` SET `sub_cid` = 135 WHERE `sub_cid` = 0 AND (`url` LIKE '%alipay%' OR `url` LIKE '%cmbchina%' OR `url` LIKE '%icbc%' OR `url` LIKE '%ccb%' OR `url` LIKE '%boc.cn%');
|
||||
|
||||
@@ -1,59 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{$title | default='YwxApp'}</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="/assets/layui/css/layui.css" rel="stylesheet">
|
||||
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="__CDN__/assets/js/html5shiv.js"></script>
|
||||
<script src="__CDN__/assets/js/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<script type="text/javascript">
|
||||
var config = {
|
||||
root: "/",
|
||||
};
|
||||
var require = {
|
||||
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<input type="text" name="title" placeholder="请输入标题" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-useradmin" lay-submit lay-filter="tableSearchButton">
|
||||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card-header"> </div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="dataTable" lay-filter="dataTable"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="dataBarTpl">
|
||||
<script type="text/html" id="dataBarTpl">
|
||||
<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="编辑节点" lay-event="update"><i class="layui-icon layui-icon-edit"></i> </a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="添加子节点" lay-event="create"><i class="layui-icon layui-icon-add-1"></i> </a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="删除节点" lay-event="delete"><i class="layui-icon layui-icon-delete"></i> </a>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 添加/编辑菜单表单模板 -->
|
||||
<script type="text/html" id="dataFormTpl">
|
||||
<!-- 添加/编辑菜单表单模板 -->
|
||||
<script type="text/html" id="dataFormTpl">
|
||||
<form class="layui-form layui-form-pane" lay-filter="wxapp-form" id="wxapp-form">
|
||||
<input type="hidden" name="id" value="{{= d.id || '' }}">
|
||||
<input type="hidden" name="pid" value="{{= d.pid || 0 }}">
|
||||
@@ -105,11 +67,11 @@
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<!-- 状态开关 -->
|
||||
<script type="text/html" id="statusTpl">
|
||||
<!-- 状态开关 -->
|
||||
<script type="text/html" id="statusTpl">
|
||||
<input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-text="启用|禁用" lay-filter="statusSwitch" {{ d.status == 1 ? 'checked' : '' }}>
|
||||
</script>
|
||||
<script type="text/html" id="dataRecybinTpl">
|
||||
<script type="text/html" id="dataRecybinTpl">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header"> </div>
|
||||
<div class="layui-card-body">
|
||||
@@ -118,303 +80,294 @@
|
||||
</div>
|
||||
</script>
|
||||
|
||||
</script>
|
||||
<script src="/assets/layui/layui.js"></script>
|
||||
<script src="/assets/ywxapp/ywxapp.js" module="backend"></script>
|
||||
<script>
|
||||
layui.use(['layer', 'http'], function () {
|
||||
var $ = layui.$;
|
||||
var treeTable = layui.treeTable;
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
var laytpl = layui.laytpl;
|
||||
var http = layui.http;
|
||||
console.log('layui.treeTable:', layui.setter.layerArea()); // 调试输出
|
||||
treeTable.render({
|
||||
defaultToolbar: [{
|
||||
title: '创建资源', layEvent: 'dataCreate', icon: 'layui-icon-add-1'
|
||||
}, {
|
||||
title: '删除资源', layEvent: 'dataDelete', icon: 'layui-icon-delete'
|
||||
}, {
|
||||
title: '资源回收站', layEvent: 'dataRecybin', icon: 'layui-icon-home'
|
||||
}, 'filter', 'exports', 'print'
|
||||
],
|
||||
elem: '#dataTable',
|
||||
url: 'category/index',
|
||||
parseData: function (res) { // 预处理返回数据
|
||||
console.log('原始数据:', res); // 调试输入
|
||||
return {
|
||||
code: res.code === 0 ? 0 : 1, // 0 标识成功
|
||||
data: res.data || [], // 数据数组
|
||||
msg: res.message || '' // 错误信息
|
||||
};
|
||||
},
|
||||
tree: {
|
||||
customName: {
|
||||
children: "children",
|
||||
isParent: "is_parent",
|
||||
name: "title",
|
||||
id: "id",
|
||||
pid: "pid",
|
||||
icon: "icon"
|
||||
},
|
||||
data: { isSimpleData: true, rootPid: 0 },
|
||||
view: {},
|
||||
async: {},
|
||||
callback: {}
|
||||
},
|
||||
height: 'full-100',
|
||||
toolbar: '#tableBarTpl',
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true, fixed: 'left' },
|
||||
{ field: 'title', title: '名称', width: 180, fixed: 'left' },
|
||||
{ field: 'icon', title: '图标', width: 80 },
|
||||
{ field: 'sort', title: '排序', width: 80, sort: true },
|
||||
{ field: 'status', title: '状态', width: 96, align: 'center', templet: '#statusTpl' },
|
||||
{ fixed: "right", title: "操作", align: "center", toolbar: "#dataBarTpl", fixed: 'right' }
|
||||
]],
|
||||
page: true,
|
||||
done: function (res, curr, count, origin) { }
|
||||
});
|
||||
treeTable.on('toolbar(dataTable)', function (obj) {
|
||||
var options = obj.config;
|
||||
switch (obj.event) {
|
||||
case 'dataCreate':
|
||||
active.dataCreate({ pid: 0, type: 1 });
|
||||
break;
|
||||
case 'dataDelete':
|
||||
var checkStatus = treeTable.checkStatus('dataTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataDelete(ids);
|
||||
break;
|
||||
case 'dataRecybin':
|
||||
active.dataRecybin();
|
||||
break;
|
||||
<script>
|
||||
layui.use(['layer', 'http'], function () {
|
||||
var $ = layui.$;
|
||||
var treeTable = layui.treeTable;
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
var laytpl = layui.laytpl;
|
||||
var http = layui.http;
|
||||
var cols = [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true, fixed: 'left' },
|
||||
{ field: 'title', title: '名称', width: 180, fixed: 'left' },
|
||||
{ field: 'icon', title: '图标', width: 80 },
|
||||
{ field: 'sort', title: '排序', width: 80, sort: true },
|
||||
{ field: 'status', title: '状态', width: 96, align: 'center', templet: '#statusTpl' },
|
||||
{ fixed: "right", title: "操作", align: "center", toolbar: "#dataBarTpl", fixed: 'right' }
|
||||
]];
|
||||
treeTable.render({
|
||||
elem: '#dataTable',
|
||||
url: 'index',
|
||||
parseData: function (res) { // 预处理返回数据
|
||||
console.log('原始数据:', res); // 调试输入
|
||||
return {
|
||||
code: res.code === 0 ? 0 : 1, // 0 标识成功
|
||||
data: res.data || [], // 数据数组
|
||||
msg: res.message || '' // 错误信息
|
||||
};
|
||||
},
|
||||
tree: {
|
||||
customName: {
|
||||
children: "children",
|
||||
isParent: "is_parent",
|
||||
name: "title",
|
||||
id: "id",
|
||||
pid: "pid",
|
||||
icon: "icon"
|
||||
},
|
||||
data: { isSimpleData: true, rootPid: 0 },
|
||||
view: {},
|
||||
async: {},
|
||||
callback: {}
|
||||
},
|
||||
height: 'full-100',
|
||||
toolbar: '#tableBarTpl',
|
||||
cols: cols,
|
||||
page: true,
|
||||
done: function (res, curr, count, origin) {
|
||||
var view = $('#dataTable').next('.layui-table-view');
|
||||
view.find('[data-perm]').each(function () {
|
||||
var perm = $(this).attr('data-perm');
|
||||
if (perm && !auth.has(perm)) { $(this).remove(); }
|
||||
});
|
||||
$('.layui-btn[data-perm]').each(function () {
|
||||
var perm = $(this).attr('data-perm');
|
||||
if (perm && !auth.has(perm)) { $(this).remove(); }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
treeTable.on('toolbar(dataTable)', function (obj) {
|
||||
var options = obj.config;
|
||||
switch (obj.event) {
|
||||
case 'dataCreate':
|
||||
active.dataCreate({ pid: 0, type: 1 });
|
||||
break;
|
||||
case 'dataDelete':
|
||||
var checkStatus = treeTable.checkStatus('dataTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataDelete(ids);
|
||||
break;
|
||||
case 'dataRecybin':
|
||||
active.dataRecybin();
|
||||
break;
|
||||
};
|
||||
});
|
||||
treeTable.on('tool(dataTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
switch (elem.event) {
|
||||
case 'update':
|
||||
active.dataEdit(data);
|
||||
break;
|
||||
case 'create':
|
||||
active.dataCreate({ pid: data.id });
|
||||
break;
|
||||
case 'delete':
|
||||
active.dataDelete([data.id]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 状态开关
|
||||
form.on('switch(statusSwitch)', function (obj) {
|
||||
var data = this.value;
|
||||
var status = obj.elem.checked ? 1 : 0;
|
||||
console.log(data);
|
||||
layer.confirm('确定要' + (status ? '启用' : '禁用') + '该数据吗?', function (index) {
|
||||
layui.http.put('update', { id: data.id,title:data.title, status: status }).then(function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg(res.msg, { icon: 1 });
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg, { icon: 2 });
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
}, function () {
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
});
|
||||
treeTable.on('tool(dataTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
switch (elem.event) {
|
||||
case 'update':
|
||||
active.dataEdit(data);
|
||||
break;
|
||||
case 'create':
|
||||
active.dataCreate({ pid: data.id });
|
||||
break;
|
||||
case 'delete':
|
||||
active.dataDelete([data.id]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
});
|
||||
|
||||
var dataFromFun = function (data, callback, done) {
|
||||
var formHtml = laytpl($('#dataFormTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: data.id ? '编辑数据' : (data.pid ? '添加子数据' : '添加根数据'),
|
||||
content: formHtml,
|
||||
anim: "slideLeft",
|
||||
offset: "r",
|
||||
btnAlign: "l",
|
||||
area: ['520px', '98%'],
|
||||
shade: 0.1,
|
||||
shadeClose: true,
|
||||
btn: ['确定', '取消'],
|
||||
success: function (layero, index) {
|
||||
callback(layero, index);
|
||||
form.render();
|
||||
},
|
||||
yes: function (index, layero) {
|
||||
window.layui.form.on('submit(wxapp-form-submit)', function (elem) {
|
||||
done(layero, index, elem);
|
||||
layui.off('submit(wxapp-form)', 'from');
|
||||
return false;
|
||||
});
|
||||
layero.contents().find("#wxapp-form-submit").trigger('click');
|
||||
}
|
||||
});
|
||||
|
||||
form.on('submit(tableSearchButton)', function (data) {
|
||||
var field = data.field;
|
||||
treeTable.reload('dataTable', {
|
||||
where: field
|
||||
});
|
||||
});
|
||||
// 状态开关
|
||||
form.on('switch(statusSwitch)', function (obj) {
|
||||
var id = this.value;
|
||||
var status = obj.elem.checked ? 1 : 0;
|
||||
layer.confirm('确定要' + (status ? '启用' : '禁用') + '该数据吗?', function (index) {
|
||||
layui.http.put('update', { id: id, status: status }).then(function (res) {
|
||||
if (res.code == 0) {
|
||||
layer.msg(res.msg, { icon: 1 });
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg, { icon: 2 });
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
}
|
||||
});
|
||||
};
|
||||
//事件
|
||||
var active = {
|
||||
dataCreate: function (data = {}) {
|
||||
dataFromFun(data,
|
||||
function (layero, index) { },
|
||||
function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
field.status = field.status ? 1 : 0;
|
||||
http.post('save', field)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
dataEdit: function (data = {}) {
|
||||
dataFromFun(data,
|
||||
function (layero, index) { },
|
||||
function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
field.status = field.status ? 1 : 0;
|
||||
http.put('update', field)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
dataDelete: function (ids, force = 0) {
|
||||
if (ids.length === 0) {
|
||||
return layer.msg('请选择数据');
|
||||
}
|
||||
layer.prompt({
|
||||
formType: 1
|
||||
, title: '敏感操作,请验证口令'
|
||||
}, function (value, index) {
|
||||
layer.close(index);
|
||||
}, function () {
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
layer.confirm('确定删除吗?', function (index) {
|
||||
http.delete('delete', { ids: ids.join(','), force: force })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
layer.msg('已删除');
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var dataFromFun = function (data, callback, done) {
|
||||
var formHtml = laytpl($('#dataFormTpl').html()).render(data || {});
|
||||
},
|
||||
dataRecybin: function (data = {}) {
|
||||
var formHtml = laytpl($('#dataRecybinTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: data.id ? '编辑数据' : (data.pid ? '添加子数据' : '添加根数据'),
|
||||
title: '资源回收站',
|
||||
content: formHtml,
|
||||
anim: "slideLeft",
|
||||
offset: "r",
|
||||
btnAlign: "l",
|
||||
area: layui.setter.layerArea(),
|
||||
area: ['60%', '99%'],
|
||||
shade: 0.1,
|
||||
shadeClose: true,
|
||||
btn: ['确定', '取消'],
|
||||
success: function (layero, index) {
|
||||
callback(layero, index);
|
||||
form.render();
|
||||
},
|
||||
yes: function (index, layero) {
|
||||
window.layui.form.on('submit(wxapp-form-submit)', function (elem) {
|
||||
done(layero, index, elem);
|
||||
layui.off('submit(wxapp-form)', 'from');
|
||||
return false;
|
||||
});
|
||||
layero.contents().find("#wxapp-form-submit").trigger('click');
|
||||
}
|
||||
});
|
||||
};
|
||||
//事件
|
||||
var active = {
|
||||
dataCreate: function (data = {}) {
|
||||
dataFromFun(data,
|
||||
function (layero, index) { },
|
||||
function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
field.icon = 'layui-icon ' + field.icon;
|
||||
field.status = field.status ? 1 : 0;
|
||||
http.post('save', field)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
dataEdit: function (data = {}) {
|
||||
dataFromFun(data,
|
||||
function (layero, index) { },
|
||||
function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
field.icon = 'layui-icon ' + field.icon;
|
||||
field.status = field.status ? 1 : 0;
|
||||
http.put('update', field)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
dataDelete: function (ids, force = 0) {
|
||||
if (ids.length === 0) {
|
||||
return layer.msg('请选择数据');
|
||||
}
|
||||
layer.prompt({
|
||||
formType: 1
|
||||
, title: '敏感操作,请验证口令'
|
||||
}, function (value, index) {
|
||||
layer.close(index);
|
||||
layer.confirm('确定删除吗?', function (index) {
|
||||
http.delete('delete', { ids: ids.join(','), force: force })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
layer.msg('已删除');
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
dataRecybin: function (data = {}) {
|
||||
var formHtml = laytpl($('#dataRecybinTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: '资源回收站',
|
||||
content: formHtml,
|
||||
anim: "slideLeft",
|
||||
offset: "r",
|
||||
area: ['60%', '99%'],
|
||||
shade: 0.1,
|
||||
shadeClose: true,
|
||||
success: function (layero, index) {
|
||||
layui.table.render({
|
||||
elem: '#dataRecybinTable',
|
||||
url: 'recyclebin',
|
||||
height: 'full-100',
|
||||
defaultToolbar: [{
|
||||
title: '批量删除数据',
|
||||
layEvent: 'dataDelete',
|
||||
icon: 'layui-icon-delete',
|
||||
onClick: function (obj) {
|
||||
var checkStatus = treeTable.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
console.log(ids);
|
||||
}
|
||||
}, 'filter', 'exports', 'print'],
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true, fixed: 'left' },
|
||||
{ field: 'title', title: '标题', width: 180, fixed: 'left' },
|
||||
{
|
||||
fixed: "right", title: "操作", width: 120, align: "center", templet: function (d) {
|
||||
return `<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm" title="恢复数据" lay-event="restore" > <i class="layui-icon layui-icon-edit"></i> </a >
|
||||
<a class="layui-btn layui-btn-sm" title="删除数据" lay-event="delete"><i class="layui-icon layui-icon-delete"></i> </a>
|
||||
</div >`;
|
||||
}
|
||||
}
|
||||
]],
|
||||
page: true,
|
||||
done: function (res, curr, count, origin) {
|
||||
layui.table.on('tool(dataRecybinTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
switch (elem.event) {
|
||||
case 'restore':
|
||||
active.dataRestore([data.id]);
|
||||
break;
|
||||
case 'delete':
|
||||
active.dataDelete([data.id], 1);
|
||||
break;
|
||||
}
|
||||
});
|
||||
layui.table.on('toolbar(dataRecybinTable)', function (elem) {
|
||||
switch (elem.event) {
|
||||
case 'dataDelete':
|
||||
var checkStatus = table.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let idss = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataDelete(idss, 1);
|
||||
break;
|
||||
case 'dataRestore':
|
||||
var checkStatus = table.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataRestore(ids);
|
||||
break;
|
||||
};
|
||||
});
|
||||
layui.table.render({
|
||||
elem: '#dataRecybinTable',
|
||||
url: 'recyclebin',
|
||||
height: 'full-100',
|
||||
defaultToolbar: [{
|
||||
title: '批量删除数据',
|
||||
layEvent: 'dataDelete',
|
||||
icon: 'layui-icon-delete',
|
||||
onClick: function (obj) {
|
||||
var checkStatus = treeTable.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
console.log(ids);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
dataRestore: function (ids) {
|
||||
if (ids.length === 0) {
|
||||
return layer.msg('请选择数据');
|
||||
}
|
||||
http.put('restore', { ids: ids.join(',') })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.msg('恢复成功');
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '恢复失败');
|
||||
}, 'filter', 'exports', 'print'],
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true, fixed: 'left' },
|
||||
{ field: 'title', title: 'title', width: 180, fixed: 'left' },
|
||||
{
|
||||
fixed: "right", title: "操作", width: 120, align: "center", templet: function (d) {
|
||||
return `<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm" title="恢复数据" lay-event="restore" > <i class="layui-icon layui-icon-edit"></i> </a >
|
||||
<a class="layui-btn layui-btn-sm" title="删除数据" lay-event="delete"><i class="layui-icon layui-icon-delete"></i> </a>
|
||||
</div >`;
|
||||
}
|
||||
}
|
||||
]],
|
||||
page: true,
|
||||
done: function (res, curr, count, origin) {
|
||||
layui.table.on('tool(dataRecybinTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
switch (elem.event) {
|
||||
case 'restore':
|
||||
active.dataRestore([data.id]);
|
||||
break;
|
||||
case 'delete':
|
||||
active.dataDelete([data.id], 1);
|
||||
break;
|
||||
}
|
||||
});
|
||||
layui.table.on('toolbar(dataRecybinTable)', function (elem) {
|
||||
switch (elem.event) {
|
||||
case 'dataDelete':
|
||||
var checkStatus = table.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let idss = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataDelete(idss, 1);
|
||||
break;
|
||||
case 'dataRestore':
|
||||
var checkStatus = table.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataRestore(ids);
|
||||
break;
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
dataRestore: function (ids) {
|
||||
if (ids.length === 0) {
|
||||
return layer.msg('请选择数据');
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
http.put('restore', { ids: ids.join(',') })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.msg('恢复成功');
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '恢复失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
</html>
|
||||
</script>
|
||||
@@ -62,18 +62,63 @@
|
||||
<div class="section-title">
|
||||
<h2>📋 {$info.title}</h2>
|
||||
</div>
|
||||
<div class="website-list">
|
||||
{volist name="info.links" id="link"}
|
||||
<a href="/haonav/site/{$link.id}.html" class="website-item">
|
||||
<span class="website-item-icon"><img src="{$link.show_icon}" loading="lazy" alt="{$link.title}"></span>
|
||||
<span class="website-item-info">
|
||||
<h3>{$link.title}</h3>
|
||||
<p>{$link.description|default='暂无描述'}</p>
|
||||
</span>
|
||||
<span class="qr-btn js-qr" data-url="{$link.url}" title="二维码">▦</span>
|
||||
</a>
|
||||
{/volist}
|
||||
|
||||
{if $mode == 'group'}
|
||||
<!-- 顶级分类:按二级子分类分组展示 -->
|
||||
{volist name="info.children" id="sub"}
|
||||
<div class="sub-category">
|
||||
<div class="sub-title"><span>{$sub.title}</span></div>
|
||||
<div class="website-list">
|
||||
{volist name="sub.sub_links" id="link"}
|
||||
<a href="/haonav/site/{$link.id}.html" class="website-item">
|
||||
<span class="website-item-icon"><img src="{$link.show_icon}" loading="lazy" alt="{$link.title}"></span>
|
||||
<span class="website-item-info">
|
||||
<h3>{$link.title}</h3>
|
||||
<p>{$link.description|default='暂无描述'}</p>
|
||||
</span>
|
||||
<span class="qr-btn js-qr" data-url="{$link.url}" title="二维码">▦</span>
|
||||
</a>
|
||||
{/volist}
|
||||
</div>
|
||||
</div>
|
||||
{/volist}
|
||||
|
||||
<!-- 未细分二级的常用链接(sub_cid=0) -->
|
||||
{notempty name="others"}
|
||||
<div class="sub-category">
|
||||
<div class="sub-title"><span>常用</span></div>
|
||||
<div class="website-list">
|
||||
{volist name="others" id="link"}
|
||||
<a href="/haonav/site/{$link.id}.html" class="website-item">
|
||||
<span class="website-item-icon"><img src="{$link.show_icon}" loading="lazy" alt="{$link.title}"></span>
|
||||
<span class="website-item-info">
|
||||
<h3>{$link.title}</h3>
|
||||
<p>{$link.description|default='暂无描述'}</p>
|
||||
</span>
|
||||
<span class="qr-btn js-qr" data-url="{$link.url}" title="二维码">▦</span>
|
||||
</a>
|
||||
{/volist}
|
||||
</div>
|
||||
</div>
|
||||
{/notempty}
|
||||
{else /}
|
||||
<!-- 二级/叶子分类:直接列出该分类下的全部链接 -->
|
||||
<div class="sub-category">
|
||||
<div class="sub-title"><span>{$info.title}</span></div>
|
||||
<div class="website-list">
|
||||
{volist name="links" id="link"}
|
||||
<a href="/haonav/site/{$link.id}.html" class="website-item">
|
||||
<span class="website-item-icon"><img src="{$link.show_icon}" loading="lazy" alt="{$link.title}"></span>
|
||||
<span class="website-item-info">
|
||||
<h3>{$link.title}</h3>
|
||||
<p>{$link.description|default='暂无描述'}</p>
|
||||
</span>
|
||||
<span class="qr-btn js-qr" data-url="{$link.url}" title="二维码">▦</span>
|
||||
</a>
|
||||
{/volist}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
|
||||
@@ -143,21 +143,28 @@
|
||||
<!-- 分类区块 -->
|
||||
<div class="category-block" id="cat-{$k}">
|
||||
<div class="section-title">
|
||||
<h2>📋 <a href="/haonav/category/{$vo.id}.html" target="_blank"> {$vo.title} </a></h2>
|
||||
<h2>📋 <a href="/haonav/category/{$vo.id}.html" target="_blank"> {$vo.title} </a></h2> <div class="sub-title"><span>全部</span> <a class="sub-more" href="/haonav/category/{$vo.id}.html">更多 ›</a></div>
|
||||
|
||||
</div>
|
||||
<div class="website-grid">
|
||||
{volist name="vo.links" id="site"}
|
||||
<div class="website-card" data-sid="{$site.id}">
|
||||
<span class="fav-btn js-fav" data-id="{$site.id}" data-title="{$site.title}" data-url="{$site.url}" data-icon="{$site.show_icon}" title="收藏">★</span>
|
||||
<span class="qr-btn js-qr" data-url="{$site.url}" title="二维码">▦</span>
|
||||
<a href="/haonav/site/{$site.id}.html" target="_blank" title="{$site.title}" style="text-decoration:none;color:inherit;display:block;">
|
||||
<div class="website-icon"><img src="{$site.show_icon}" loading="lazy" alt="{$site.title}"></div>
|
||||
<div class="website-name">{$site.title}</div>
|
||||
{if $site.rating_count > 0}<div class="website-rating">⭐ {$site.rating}<span class="rc">({$site.rating_count})</span></div>{/if}
|
||||
</a>
|
||||
|
||||
<!-- 首页只展示一级分类下的全部链接,二级分类在分类页展示 -->
|
||||
{notempty name="vo.links"}
|
||||
<div class="sub-category">
|
||||
<div class="website-grid">
|
||||
{volist name="vo.links" id="site"}
|
||||
<div class="website-card" data-sid="{$site.id}">
|
||||
<span class="fav-btn js-fav" data-id="{$site.id}" data-title="{$site.title}" data-url="{$site.url}" data-icon="{$site.show_icon}" title="收藏">★</span>
|
||||
<span class="qr-btn js-qr" data-url="{$site.url}" title="二维码">▦</span>
|
||||
<a href="/haonav/site/{$site.id}.html" target="_blank" title="{$site.title}" style="text-decoration:none;color:inherit;display:block;">
|
||||
<div class="website-icon"><img src="{$site.show_icon}" loading="lazy" alt="{$site.title}"></div>
|
||||
<div class="website-name">{$site.title}</div>
|
||||
{if $site.rating_count > 0}<div class="website-rating">⭐ {$site.rating}<span class="rc">({$site.rating_count})</span></div>{/if}
|
||||
</a>
|
||||
</div>
|
||||
{/volist}
|
||||
</div>
|
||||
{/volist}
|
||||
</div>
|
||||
{/notempty}
|
||||
</div>
|
||||
{/volist}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user