no message

This commit is contained in:
hellcat 2026-07-04 19:01:35 +08:00
parent eddaede368
commit 82d5058db0
34 changed files with 908 additions and 134 deletions

View File

@ -22,8 +22,10 @@ abstract class ListController
];
protected $oModel;
protected array $aValueFields = [];
protected string $sPageName = '';
protected string $sModelName = '';
// protected string $sModelName = '';
protected string $sModelPath = '';
protected array $aFilterFields = []; // 允许where的字段
// protected array $aArticleFields = []; // 文章字段
protected array $aFieldTypes = [];
@ -32,7 +34,7 @@ abstract class ListController
protected array $aListFields = []; // list展示的字段和名字映射
protected array $aDateFields = []; // date字段
protected array $aJsonFields = []; // json字段
protected array $aValueStyle = []; // 特定文字渲染样式
// protected array $aValueStyle = []; // 特定文字渲染样式
protected int $iLimitMax = 100; // 默认limit限制
protected int $iLimit = 20; // 默认显示行数
protected array $aCcExtFields = []; // 自定义条件字段追加在listFields后
@ -44,20 +46,40 @@ abstract class ListController
"sSort" => "desc"
]
];
protected array $aValueStyle = [ // 特定文字渲染样式
'关闭' => "<span class='text-danger'>关闭</span>",
'开启' => "<span class='text-success'>开启</span>"
];
public function __construct()
{
if ($this->sModelName) {
$sFullNamespace = "App\Models\Nasa\\{$this->sModelName}";
if (! class_exists($sFullNamespace)) {
throw new InvalidArgumentException("【系统架构错误】: 找不到领域模型 {$sFullNamespace}");
if ($this->sModelPath) {
if (! class_exists($this->sModelPath)) {
throw new InvalidArgumentException("【系统架构错误】: 找不到领域模型 {$this->sModelPath}");
}
$this->oModel = new $sFullNamespace();
$this->oModel = new $this->sModelPath();
$this->aCcFields = array_merge($this->aListFields, $this->aCcExtFields);
$this->aCcFields = $this->getCcFields();
}
}
private function getCcFields()
{
$a = [];
foreach ($this->aCcFields as $k => $v) {
if (is_array($v)) {
foreach ($v as $kk => $vv) {
$a[$kk] = $vv;
}
} else {
$a[$k] = $v;
}
}
return $a;
}
private function mapOperator($sOperator)
{
$sSqlOperator = match ($sOperator) {
@ -170,7 +192,7 @@ abstract class ListController
'iCode' => 200,
'aData' => [
'cList' => $cList,
'aMap' => $this->oModel->getMapAll(),
'aMap' => $this->getMapAll(),
'aListField' => $this->getListFields(),
'sPageName' => $this->sPageName
]
@ -205,7 +227,7 @@ abstract class ListController
'iCode' => 200,
'aData' => [
'cList' => $cList,
'aMap' => $this->oModel->getMapAll(),
'aMap' => $this->getMapAll(),
'aListField' => $this->getListFields(),
'sPageName' => $this->sPageName
]
@ -336,11 +358,15 @@ abstract class ListController
public function getMapAll()
{
$a = [];
if (method_exists($this->oModel, 'getMapAll')) {
return $this->oModel->getMapAll();
$a = $this->oModel->getMapAll();
}
return [];
$b = array_merge($a, $this->aValueFields);
return $b;
}
public function index(Request $oRequest)
@ -428,7 +454,9 @@ abstract class ListController
$oQuery->orderBy($aOrderBy['sField'], $aOrderBy['sSort']);
}
$oPaginatedData = $oQuery->paginate($iLimit, array_keys($this->aListFields));
$oPaginatedData = $oQuery->paginate($iLimit);
// tt($oPaginatedData);
// $oPaginatedData = $oQuery->paginate($iLimit, array_keys($this->aListFields));
return response()->json([
'iCode' => 200,

View File

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\Nasa\V1;
use Illuminate\Http\Request;
use App\Http\Controllers\Api\Nasa\V1\Base\ListController;
final class FnCrawController extends ListController
{
protected string $sModelPath = 'App\Models\CrawlFreenode';
protected array $aFilterFields = [ // 允许where的字段不设视为全允许
// 's_name',
// 'i_status'
];
protected string $sPageName = "fn爬取设置";
protected array $aListFields = [ // list展示的字段和名字映射
'id' => 'id',
'name' => 'name',
'group' => 'group',
'url_base' => '爬取网站',
'url_node' => '爬取网站路径',
'url_file' => '爬取文件名',
'file_real' => '保存文件名',
'status' => 'status',
];
protected array $aValueStyle = [ // 特定文字渲染样式
'关闭' => "<span class='text-danger'>关闭</span>",
'开启' => "<span class='text-success'>开启</span>"
];
protected array $aFieldTypes = [
'url_file' => 'json',
'file_real' => 'json',
];
protected array $aOrderBy = [
[
"sField" => "status",
"sSort" => "desc",
],
[
"sField" => "id",
"sSort" => "desc",
]
];
protected array $aJsonFields = [ // json字段
];
protected array $aDateFields = [ // date字段
// 'dtUpdatedAt'
];
protected array $aLenthFields = [ // 限制长度的字段
// "xx" => 100,
];
protected array $aSelectFields = [ // 作为默认select的字段
'status',
];
protected array $aValueFields = [ // 值映射
'status' => [
'1' => '开启',
'0' => '关闭',
]
];
protected array $aCcExtFields = [
// 'dtCreatedAt' => '创建时间'
];
// protected array $aLimit = [3, 15, 40, 100]; // list长度第一个为默认
}

View File

@ -8,7 +8,7 @@ use App\Http\Controllers\Api\Nasa\V1\Base\ListController;
final class SettingNavController extends ListController
{
protected string $sModelName = 'Navigation';
protected string $sModelPath = 'App\Models\Nasa\Navigation';
protected array $aFilterFields = [ // 允许where的字段不设视为全允许
// 's_name',
// 'i_status'

View File

@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\Nasa\V1;
use Illuminate\Http\Request;
use App\Http\Controllers\Api\Nasa\V1\Base\ListController;
final class TestsFnSubfileController extends ListController
{
protected string $sModelPath = 'App\Models\TestSubfile';
protected array $aFilterFields = [ // 允许where的字段不设视为全允许
// 's_name',
// 'i_status'
];
protected string $sPageName = "订阅文件状态";
protected array $aListFields = [ // list展示的字段和名字映射
'id' => 'id',
'site_code'=> 'site code',
'url' => [
'clash_url' => 'clash url',
'v2ray_url' => 'v2ray url',
'singbox_url' => 'singbox url'
],
'content' => [
'clash_content' => 'clash file',
'v2ray_content' => 'v2ray file',
'singbox_content' => 'singbox file'
],
'updated_at'=> '更新时间'
];
protected array $aFieldTypes = [
'clash_url' => 'copy',
'v2ray_url' => 'copy',
'singbox_url' => 'copy',
'clash_content' => 'copy-nick',
'v2ray_content' => 'copy-nick',
'singbox_content' => 'copy-nick'
];
protected array $aOrderBy = [
[
"sField" => "id",
"sSort" => "desc",
]
];
protected array $aJsonFields = [ // json字段
];
protected array $aDateFields = [ // date字段
// 'dtUpdatedAt'
];
protected array $aLenthFields = [ // 限制长度的字段
// "xx" => 100,
];
protected array $aSelectFields = [ // 作为默认select的字段
// 'iStatus',
// 'sComponentType',
];
// protected array $aValueFields = [ // 值映射
// 'sComponentType' => [
// 'TOP_BAR' => '顶级菜单',
// 'SIDEBAR_MENU' => '次级菜单',
// 'INNER_PAGE_TAB' => '页内菜单',
// ]
// ];
protected array $aCcExtFields = [
// 'dtCreatedAt' => '创建时间'
];
// protected array $aLimit = [3, 15, 40, 100]; // list长度第一个为默认
}

View File

@ -97,6 +97,10 @@ class CmdController
$sUrl = "https://".$sUrl;
if ($sUrl = "dd.loc/api/tg/hook") {
$sUrl = "http://".$sUrl;
}
$aData = [
"shell" => "cmd",
'update_id' => 123456789,

View File

@ -7,6 +7,7 @@ use App\Http\Requests\Nasa\NasaQueryRequest;
use Illuminate\View\View;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Arr;
use App\Models\Nasa\Book as NasaBook;
class ListController
{
@ -14,6 +15,37 @@ class ListController
protected string $sView = 'nasa._commons.list';
protected string $sSiteWww = '';
protected string $sHeaderActionView = 'nasa.v1._actions.list';
protected string $sListView = 'nasa.v1._lists.list';
protected array $aBookCode = [];
public function getNasaBook()
{
$cNasaBook = NasaBook::whereIn("code", $this->aBookCode)->get();
$aExistingCodes = $cNasaBook->pluck('code')->toArray();
$aMissingCodes = array_diff($this->aBookCode, $aExistingCodes);
if (!empty($aMissingCodes)) {
$aInsertData = [];
$sNow = now()->toDateTimeString(); // 保持大厂规范:批量插入需要手动补齐时间戳
foreach ($aMissingCodes as $sCode) {
$aInsertData[] = [
'code' => $sCode,
'name' => $sCode,
'created_at' => $sNow,
'updated_at' => $sNow,
];
}
NasaBook::insert($aInsertData);
$cNasaBook = NasaBook::whereIn("code", $this->aBookCode)->get();
}
return $cNasaBook;
}
public function index(NasaQueryRequest $oRequest): View
{
@ -23,6 +55,9 @@ class ListController
$aResponse['sApiPath'] = $this->sApiPath;
$aResponse['sSiteWww'] = $this->sSiteWww;
$aResponse['sHeaderActionView'] = $this->sHeaderActionView;
$aResponse['sListView'] = $this->sListView;
$aResponse['cNasaBook'] = $this->getNasaBook();
return view($this->sView, $aResponse);
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers\Web\Nasa\V1;
use Illuminate\Http\Request;
//use App\Http\Requests\Nasa\NasaQueryRequest;
//use Illuminate\View\View;
//use Illuminate\Support\Facades\Http;
//use App\Http\Controllers\Web\Nasa\V1\Base\ListController;
use App\Models\Nasa\Book as NasaBook;
class BookController
{
public function find()
{
$iId = request('id');
$oNasaBook = NasaBook::find($iId);
return response()->json([
"code" => 200,
"data" => $oNasaBook->toArray(),
], 200);
}
public function save()
{
$iId = request('id');
$sContent = request('content');
$oNasaBook = NasaBook::find($iId);
$oNasaBook->content = $sContent;
$oNasaBook->save();
return response()->json([
"code" => 200,
"sMsg" => "保存成功",
], 200);
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers\Web\Nasa\V1;
use Illuminate\Http\Request;
use App\Http\Requests\Nasa\NasaQueryRequest;
use Illuminate\View\View;
use Illuminate\Support\Facades\Http;
use App\Http\Controllers\Web\Nasa\V1\Base\ListController;
class FuAnnBaseController extends ListController
{
protected string $sApiPath = 'ann/base';
protected string $sView = 'nasa._commons.list';
protected string $sSiteWww;
protected string $sHeaderActionView = 'nasa.v1._actions.list';
public function __construct()
{
$this->sSiteWww = config("path.url_ship_fu_base") ?? '';
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Web\Nasa\V1;
use Illuminate\Http\Request;
use App\Http\Requests\Nasa\NasaQueryRequest;
use Illuminate\View\View;
use Illuminate\Support\Facades\Http;
use App\Http\Controllers\Web\Nasa\V1\Base\ListController;
class MasterFnCrawController extends ListController
{
protected string $sApiPath = "fn/craw";
protected string $sView = 'nasa._commons.list';
protected string $sSiteWww;
protected string $sHeaderActionView = 'nasa.v1._actions.list';
protected array $aBookCode = [
"fn爬取说明书",
];
public function __construct()
{
$this->sSiteWww = config("path.url_master_base") ?? '';
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers\Web\Nasa\V1;
use Illuminate\Http\Request;
use App\Http\Requests\Nasa\NasaQueryRequest;
use Illuminate\View\View;
use Illuminate\Support\Facades\Http;
use App\Http\Controllers\Web\Nasa\V1\Base\ListController;
class TestsFnSubfileController extends ListController
{
protected string $sApiPath = 'tests/fn/subfile';
// protected string $sView = 'nasa._commons.list';
protected string $sSiteWww;
protected string $sHeaderActionView = 'nasa.v1._actions.none';
// protected string $sListView = 'nasa.v1._lists.subfile';
protected array $aBookCode = [
"tests_subfile",
"test",
"new"
];
public function __construct()
{
$this->sSiteWww = config("path.url_master_base") ?? '';
}
}

14
app/Models/Nasa/Book.php Normal file
View File

@ -0,0 +1,14 @@
<?php
namespace App\Models\Nasa;
//use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
//use Illuminate\Database\Eloquent\Relations\HasMany;
//use Illuminate\Support\Facades\Route;
class Book extends Model
{
protected $table = 'nasa_book';
}

View File

@ -126,6 +126,7 @@ class Navigation extends Model
}
// 2. 兜底逻辑:没有下级,或者所有下级分支都没配置有效路由,返回自身的 URL
// return $this->sPermissionSlug;
return Route::has($this->sPermissionSlug) ? route($this->sPermissionSlug) : '';
}

20
app/Models/TestSubfile.php Executable file
View File

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TestSubfile extends Model
{
protected $table = 'test_subfile';
// public $timestamps = false;
protected function casts(): array
{
return [
// 只要模型一出库,全自动帮你把 T 和 Z 剃掉,换成干净的年月日时分秒
'created_at' => 'datetime:Y-m-d H:i:s',
'updated_at' => 'datetime:Y-m-d H:i:s',
];
}
}

View File

@ -17,6 +17,7 @@ use App\Services\Cron\FnTgPublish as CronFnTgPublish;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
use App\Services\CacheTg as ServiceCacheTg;
use App\Services\Cron\FnSitePublish as CronFnSitePublish;
use App\Services\Cron\NasaTestFnSubfile;
final class Cron
{
@ -55,6 +56,7 @@ final class Cron
if ($h % 3 === 0 && $i === 3) {
CronFreenodeSync::nodehub(); // fn分到到nodehub
CronFreenodeSync::run(); // fn分发到各到各站旧版
NasaTestFnSubfile::run_b();
}
if ($h == 11 && $i == 3) {

View File

@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace App\Services\Cron;
use App\Models\TestSubfile;
use App\Services\TomTool\Telegram\Slave as TeleSlave;
//use App\Services\TomTool\Telegram\Master as TeleMaster;
use App\Models\SiteMap as SiteMap;
use Illuminate\Support\Facades\Http;
use App\Services\FreenodeHelperService;
use Illuminate\Support\Facades\DB;
final class NasaTestFnSubfile
{
public static function run_b()
{
echo "\n\n 运行监测-fn_b start";
$sDateCode = date("Ymd");
// $sDateCode = "20260211"; // test
$sDateToday = date("Y-m-d");
$sDateToday = "2026-02-26";
$aFnClients = FreenodeHelperService::getClients();
$oNodeBus = SiteMap::where("group_code", "nodehub")->first();
$cFnSite = SiteMap::whereIn("group_code", ["fn_b"])->get(); // a系列逐渐抛弃
// foreach ($cFnSite as $oFnSite) {
//
// // model
// $oTestSubfile = TestSubfile::where("site_code", $oFnSite->code_short)->whereDate("created_at", $sDateToday)->first();
//
// if (!$oTestSubfile) {
// $oTestSubfile = new TestSubfile();
// $oTestSubfile->site_code = $oFnSite->code_short;
// }
//
// // 页面监测
// $sUrlHtml = "https://".$oFnSite->url."/a/fn".$sDateCode.".html";
// $oResponse = Http::withoutVerifying()->get($sUrlHtml); // 不验证ssl
// $iHttpStatus = $oResponse->status() ?? 0;
//
// if ($iHttpStatus != 200) {
// TeleSlave::warn()->send("运行监测-fn_b:有页面未获取成功");
// }
//
// $oTestSubfile->status_http_page = $iHttpStatus;
//
// // subfile监测
// foreach ($aFnClients as $sFnClient) {
// $sUrlSubFile = FreenodeHelperService::urlFeedToday($oFnSite->code_short, $sFnClient);
// $oResponse = Http::timeout(60)
// ->withoutVerifying()
// ->withHeaders([
// 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
// 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
// 'Accept-Language' => 'zh-CN,zh;q=0.9,en;q=0.8',
// ])
// ->get($sUrlSubFile);
//
// $iHttpStatus = $oResponse->status() ?? 0;
//
// $sBody = $iHttpStatus;
//
// if ($iHttpStatus == 200) {
// $sBody = $oResponse->body();
// } else {
// TeleSlave::warn()->send("运行监测-fn_b:有subfile未获取成功");
// }
//
// $oTestSubfile->{$sFnClient."_url"} = "https://".$sUrlSubFile;
// $oTestSubfile->{$sFnClient."_content"} = $sBody;
// }
//
// $oTestSubfile->save();
//
// }
//// _admin
$oTestSubfile = TestSubfile::where("site_code", "_admin")->whereDate("created_at", $sDateToday)->first();
if (!$oTestSubfile) {
$oTestSubfile = new TestSubfile();
$oTestSubfile->site_code = "_admin";
}
foreach ($aFnClients as $sFnClient) {
$sFilePath = $_ENV["dir_base"]."public/freenode/merge/_admin/".$sFnClient."/".$sDateToday.".txt";
$file = file_get_contents($sFilePath);
if (!file_exists($sFilePath)) {
continue;
}
$oTestSubfile->{$sFnClient."_url"} = $sFilePath;
$oTestSubfile->{$sFnClient."_content"} = $file;
}
$oTestSubfile->save();
// 删除超过30天的log
$sDatePrev30 = date("Y-m-m", strtotime("-30 days"));
TestSubfile::whereDate("created_at", "<", $sDatePrev30)->delete();
echo "\n 运行监测-fn_b done";
}
}

View File

@ -27,6 +27,17 @@ class FreenodeHelperService
return $sFeedUrl;
}
public static function getClients()
{
$aClients = [
"clash",
"v2ray",
// "singbox",
];
return $aClients;
}
public static function clientToExt($sClient)
{
switch ($sClient) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -20,12 +20,12 @@
"src": "node_modules/remixicon/fonts/remixicon.woff2"
},
"resources/css/app.css": {
"file": "assets/app-gdOSk-Nv.css",
"file": "assets/app-Bh730XHi.css",
"src": "resources/css/app.css",
"isEntry": true
},
"resources/js/app.js": {
"file": "assets/app-zmCj5tAh.js",
"file": "assets/app-Db_h2PL5.js",
"name": "app",
"src": "resources/js/app.js",
"isEntry": true

View File

@ -1,3 +1,9 @@
.u-truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* ==========================================================================
基础 Item / Meta
========================================================================== */

View File

@ -4,6 +4,8 @@ import $ from 'jquery';
import './nasa/components/filter';
import './nasa/components/tomAlert';
import './nasa/components/tomModal';
import './nasa/components/nasabook';
import './nasa/components/copy';
import './nasa/page/list';
import './nasa/page/detail';
import './nasa/page/login';

View File

@ -0,0 +1,92 @@
import $ from 'jquery';
// 1. 全局单例事件绑定(事件委托:性能拉满)
$(document).on('click', '.js-copy', function() {
// oCurrentBtn: 当前被点击的那个按钮对象 (Object)
var oCurrentBtn = $(this);
// sTargetText: 精准获取当前按钮上绑定的文本 (String)
var sTargetText = oCurrentBtn.data('text');
// 如果没数据,直接拦截,防止复制空字符串
if (!sTargetText) {
fnShowToast(oCurrentBtn, '无复制内容');
return;
}
// 执行核心复制逻辑
if (navigator.clipboard) {
navigator.clipboard.writeText(sTargetText)
.then(function() {
fnShowToast(oCurrentBtn, '复制成功');
})
.catch(function(oError) {
console.error('Clipboard API 失败,尝试兜底: ', oError);
fnFallbackCopy(sTargetText, oCurrentBtn);
});
} else {
// 走兜底方案,并把当前按钮对象传进去
fnFallbackCopy(sTargetText, oCurrentBtn);
}
});
/**
* 2. 兜底复制方法去除了所有 alert全部改为局部提示
* @param {string} sText
* @param {object} oElement jQuery对象
*/
function fnFallbackCopy(sText, oElement) {
// oTextArea: 临时文本框对象 (Object)
var oTextArea = document.createElement("textarea");
oTextArea.value = sText;
// 样式隐形
oTextArea.style.position = "fixed";
oTextArea.style.top = "0";
oTextArea.style.left = "0";
oTextArea.style.opacity = "0";
document.body.appendChild(oTextArea);
oTextArea.focus();
oTextArea.select();
try {
// 执行老版复制指令
var sSuccess = document.execCommand('copy');
if (sSuccess) {
fnShowToast(oElement, '复制成功');
} else {
fnShowToast(oElement, '复制失败');
}
} catch (oErr) {
console.error('ExecCommand 彻底崩了: ', oErr);
fnShowToast(oElement, '复制出错');
}
// 及时销毁 DOM 节点,防止内存泄漏
document.body.removeChild(oTextArea);
}
/**
* 3. 优雅的局部提示组件高内聚职责单一
* @param {object} oElement jQuery对象
* @param {string} sMsg 提示文字
*/
function fnShowToast(oElement, sMsg) {
if (oElement.prop('disabled')) {
return;
}
sMsg = "<span class='text-success'>"+sMsg+"</span>";
var sOriginalText = oElement.html();
// 改变文字并禁用按钮,防止二次点击带来连续 DOM 操作
oElement.html(sMsg).prop('disabled', true);
// 1.5秒后恢复原状
setTimeout(function() {
oElement.html(sOriginalText).prop('disabled', false);
}, 1500);
}

View File

@ -0,0 +1,21 @@
import $ from 'jquery';
$(document).on('click', '.js-nasabook-load', function() {
let iNasaBookId = $(this).attr("nasabook-id");
let sApiUrl = '/nasa/book/'+iNasaBookId;
let oModal = $('.tom-modal[tom-modal-name="nasabook"]');
$.ajax({
url: sApiUrl,
type: 'GET',
dataType: 'json',
success: function(json) {
oModal.find(".js-tom-modal-save-input").html(json.data.content);
oModal.find(".tom-modal-header").find("span").html(json.data.name);
oModal.find(".js-tom-modal-save").attr("tom-modal-save-id", iNasaBookId);
tomModalShow("nasabook");
}
});
});

View File

@ -20,8 +20,6 @@ $(document).on('keydown', '.js-tom-modal-save-input', function(oEvent) {
return;
}
oEvent.preventDefault();
let oCurrentInput = $(this);
let sField = oCurrentInput.attr('field');
@ -39,6 +37,7 @@ $(document).on('keydown', '.js-tom-modal-save-input', function(oEvent) {
let bCurrentState = oCheckbox.prop('checked');
oCheckbox.prop('checked', !bCurrentState);
oCheckbox.val(bCurrentState ? '0' : '1');
oEvent.preventDefault();
}
});

View File

@ -2,18 +2,21 @@
<input hidden id="api_path" value="{{ $sApiPath }}">
<input hidden id="site_www" value="{{ $sSiteWww }}">
@include($sHeaderActionView ?? 'nasa.v1._actions.list')
<!--
<div class="mac-card-small flex-align-center text-muted">
<button class="mac-btn-secondary text-secondary mac-input-medium js-list-add-trigger">添加</button>
<button class="mac-btn-secondary text-secondary mac-input-medium">导航缓存:更新</button>
<div class="mac-card-small flex-align-center justify-between text-muted">
<div class="flex-center gap8" style="height:30px;">
@foreach ($cNasaBook as $oNasaBook)
<a nasabook-id="{{ $oNasaBook->id }}" class="text-muted js-nasabook-load"><i class="ri-book-line"></i> {{ $oNasaBook->name }}<span class="test-muted">{{ strlen($oNasaBook->content) }}</span></a>
@endforeach
</div>
-->
<div>@include($sHeaderActionView ?? 'nasa.v1._actions.none')</div>
</div>
@if (count($aData['aSelectField']) > 0)
<div class="mac-card-small flex-align-center text-muted js-condition">
@foreach ($aData['aSelectField'] as $k)
@php
$sFieldName = $aData['aListField'][$k] ?? $k;
$sFieldName = is_array($sFieldName)? $k : $sFieldName;
@endphp
@if (isset($aData['aMap'][$k]))
@ -92,49 +95,7 @@
</div>
<div class="mac-table-container">
<table class="mac-table">
<thead>
<tr>
@foreach ($aData["aListField"] as $k => $v)
<th>{{$v}}</th>
@endforeach
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach ($aData["cList"] as $aRow)
<tr>
@foreach ($aRow as $k => $v)
@php
$sValueType = $aData["aFieldTypes"][$k] ?? '';
$sMapValue = $aData["aMap"][$k][$v] ?? $v;
$sShowValue = $aData["aValueStyle"][$sMapValue] ?? $sMapValue;
if ($sMapValue != $v) {
$sShowValue = $sShowValue." <span class='text-muted'>(".$v.")</span>";
}
if ($sValueType == 'article') {
$sShowValue = "<a class='text-info js-tom-modal-open-article' tom-modal-field='".$k."' tom-modal-save-id='".$aRow['id']."'>文章 <span class='text-muted'>(".strlen($v).")</span></a>";
} else if ($sValueType == 'json') {
$sShowValue = "<a class='text-info js-tom-modal-open-json' tom-modal-field='".$k."' tom-modal-save-id='".$aRow['id']."'>json <span class='text-muted'>(".strlen($v).")</span></a>";
} else if ($sValueType == 'byte') {
$sShowValue = "<span class=''>".(int)($v / 1024 / 1024 / 1204)."g <span class='text-muted'>(".$v.")</span></span>";
} else if ($sValueType == 'text') {
$sShowValue = "<a class='text-info js-tom-modal-open-text' tom-modal-field='".$k."' tom-modal-save-id='".$aRow['id']."'>文本 <span class='text-muted'>(".strlen($v).")</span></a>";
}
@endphp
<td>{!! $sShowValue !!}</td>
@endforeach
<td>
<a class="mac-table-operation-btn text-muted js-open-detail" open-detail-id="{{ $aRow['id'] }}"><i class="ri-article-line"></i></a>
<!-- <a class="js-tom-modal-open" tom-modal-open="detail" hidden></a> -->
<a class="text-muted js-tom-modal-open" tom-modal-open="tool" tom-modal-data-id="{{ $aRow['id'] }}"><i class="ri-tools-fill"></i></a>
</td>
</tr>
@endforeach
</tbody>
</table>
@include($sListView ?? 'nasa.v1._lists.list')
</div>
<div class="mac-pager-bar">
@ -258,6 +219,26 @@
</div>
</div>
<div class="tom-modal" tom-modal-name="nasabook" tabindex="-1" tom-modal-lock="true" hidden>
<div class="tom-modal-inner">
<div class="tom-modal-header">
<span class="text-muted">nasa book</span>
<a class="text-muted js-tom-modal-close"></a>
</div>
<div class="tom-modal-body-flex">
<textarea class="js-tom-modal-save-input" field="content" style="min-width:1200px; min-height:800px;"></textarea>
<input type="checkbox" class="js-tom-modal-save-checked" field="content" value="1" lock checked hidden>
</div>
<div class="tom-modal-footer">
<button class="mac-btn-primary width-full js-tom-modal-save"
tom-modal-save-id=""
tom-modal-save-url="/nasa/book/"
tom-modal-save-close="true"
>确定</button>
</div>
</div>
</div>
<div class="tom-modal" tom-modal-name="order" tabindex="-1" tom-modal-mark="mask" tom-modal-lock="false" hidden>
<div class="tom-modal-inner">
<div class="tom-modal-header">
@ -276,6 +257,7 @@
<select class="text-p text-primary js-page-orderby-field">
<option value="">--- 字段 ---</option>
@foreach ($aData['aListField'] as $k => $v)
@continue (is_array($v))
<option value="{{ $k }}" @if ($aOrderBySchema['sField'] == $k) selected @endif>{{ $v }}</option>
@endforeach
</select>
@ -291,6 +273,7 @@
<select class="text-p text-primary js-page-orderby-field">
<option value="">--- 字段 ---</option>
@foreach ($aData['aListField'] as $k => $v)
@continue (is_array($v))
<option value="{{ $k }}">{{ $v }}</option>
@endforeach
</select>

View File

@ -1,3 +1 @@
<div class="mac-card-small flex-align-center text-muted">
<button class="mac-btn-secondary text-secondary mac-input-medium js-list-add-trigger">添加</button>
</div>

View File

@ -1,4 +1,2 @@
<div class="mac-card-small flex-align-center text-muted">
<button class="mac-btn-secondary text-secondary mac-input-medium js-list-add-trigger">添加</button>
<button class="mac-btn-secondary text-secondary mac-input-medium">导航缓存:更新</button>
</div>

View File

@ -1,7 +1,6 @@
<div class="mac-card-small flex-align-center text-muted">
<button class="mac-btn-secondary text-secondary mac-input-medium js-list-add-trigger">添加</button>
<button class="mac-btn-secondary text-secondary mac-input-medium js-list-node-fly">fly</button>
</div>
<script type="module">

View File

@ -0,0 +1 @@
no action

View File

@ -0,0 +1,80 @@
<table class="mac-table">
<thead>
<tr>
@foreach ($aData["aListField"] as $k => $v)
@if (is_array($v))
<th>{{$k}}</th>
@else
<th>{{$v}}</th>
@endif
@endforeach
<th>操作</th>
</tr>
</thead>
<tbody>
@php
function showValue($id, $k, $v, $aData, $sFieldNick = '')
{
// echo "<pre>";var_dump($aData["aMap"]);exit;
$sValueType = $aData["aFieldTypes"][$k] ?? '';
$sMapValue = $aData["aMap"][$k][$v] ?? $v;
$sShowValue = $aData["aValueStyle"][$sMapValue] ?? $sMapValue;
if ($sMapValue != $v) {
$sShowValue = $sShowValue." <span class='text-muted'>(".$v.")</span>";
}
if ($sValueType == 'article') {
$sShowValue = "<a class='text-info js-tom-modal-open-article' tom-modal-field='".$k."' tom-modal-save-id='".$id."'>文章 <span class='text-muted'>(".strlen($v).")</span></a>";
} else if ($sValueType == 'json') {
$sShowValue = "<a class='text-info js-tom-modal-open-json' tom-modal-field='".$k."' tom-modal-save-id='".$id."'>json <span class='text-muted'>(".strlen($v).")</span></a>";
} else if ($sValueType == 'byte') {
$sShowValue = "<span class=''>".(int)($v / 1024 / 1024 / 1204)."g <span class='text-muted'>(".$v.")</span></span>";
} else if ($sValueType == 'text') {
$sShowValue = "<a class='text-info js-tom-modal-open-text' tom-modal-field='".$k."' tom-modal-save-id='".$id."'>文本 <span class='text-muted'>(".strlen($v).")</span></a>";
} else if ($sValueType == 'text-full') {
$sShowValue = "<a class='text-info js-tom-modal-open-text' tom-modal-field='".$k."' tom-modal-save-id='".$id."'>".$v."</a>";
} else if ($sValueType == 'copy') {
$sShowValue = "<a class='text-info js-copy' data-text='".$v."' tom-modal-field='".$k."' tom-modal-save-id='".$id."'>".$v."</a>";
} else if ($sValueType == 'copy-nick') {
$sShowValue = "<a class='text-info js-copy' data-text='".$v."' tom-modal-field='".$k."' tom-modal-save-id='".$id."'>".
$sFieldNick." <span class='text-muted'>(".strlen($v).")</span>".
"<span class='text-muted' style='width:100px; display:inline-block; vertical-align: middle; word-break: break-all; max-height: 20px;'>&nbsp;".$v."</span>".
"</a>";
}
return $sShowValue;
}
@endphp
@foreach ($aData["cList"] as $aRow)
<tr>
@foreach ($aData['aListField'] as $sField => $sFieldV)
@if (is_array($sFieldV))
@php $sShowValue = ''; @endphp
@foreach ($sFieldV as $kk => $vv)
@php
$sShowValue .= '<p>'.showValue($aRow['id'], $kk, $aRow[$kk], $aData, $vv).'</p>';
@endphp
@endforeach
@else
@php
$sShowValue = showValue($aRow['id'], $sField, $aRow[$sField], $aData, $sFieldV);
@endphp
@endif
<td>{!! $sShowValue !!}</td>
@endforeach
<td>
<a class="mac-table-operation-btn text-muted js-open-detail" open-detail-id="{{ $aRow['id'] }}"><i class="ri-article-line"></i></a>
<a class="text-muted js-tom-modal-open" tom-modal-open="tool" tom-modal-data-id="{{ $aRow['id'] }}"><i class="ri-tools-fill"></i></a>
</td>
</tr>
@endforeach
</tbody>
</table>

View File

@ -0,0 +1,43 @@
<table class="mac-table">
<thead>
<tr>
@foreach ($aData["aListField"] as $k => $v)
<th>{{$v}}</th>
@endforeach
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach ($aData["cList"] as $aRow)
<tr>
@foreach ($aRow as $k => $v)
@php
$sValueType = $aData["aFieldTypes"][$k] ?? '';
$sMapValue = $aData["aMap"][$k][$v] ?? $v;
$sShowValue = $aData["aValueStyle"][$sMapValue] ?? $sMapValue;
if ($sMapValue != $v) {
$sShowValue = $sShowValue." <span class='text-muted'>(".$v.")</span>";
}
if ($sValueType == 'article') {
$sShowValue = "<a class='text-info js-tom-modal-open-article' tom-modal-field='".$k."' tom-modal-save-id='".$aRow['id']."'>文章 <span class='text-muted'>(".strlen($v).")</span></a>";
} else if ($sValueType == 'json') {
$sShowValue = "<a class='text-info js-tom-modal-open-json' tom-modal-field='".$k."' tom-modal-save-id='".$aRow['id']."'>json <span class='text-muted'>(".strlen($v).")</span></a>";
} else if ($sValueType == 'byte') {
$sShowValue = "<span class=''>".(int)($v / 1024 / 1024 / 1204)."g <span class='text-muted'>(".$v.")</span></span>";
} else if ($sValueType == 'text') {
$sShowValue = "<a class='text-info js-tom-modal-open-text' tom-modal-field='".$k."' tom-modal-save-id='".$aRow['id']."'>文本 <span class='text-muted'>(".strlen($v).")</span></a>";
}
@endphp
<td>{!! $sShowValue !!}</td>
@endforeach
<td>
<a class="mac-table-operation-btn text-muted js-open-detail" open-detail-id="{{ $aRow['id'] }}"><i class="ri-article-line"></i></a>
<!-- <a class="js-tom-modal-open" tom-modal-open="detail" hidden></a> -->
<a class="text-muted js-tom-modal-open" tom-modal-open="tool" tom-modal-data-id="{{ $aRow['id'] }}"><i class="ri-tools-fill"></i></a>
</td>
</tr>
@endforeach
</tbody>
</table>

View File

@ -3,6 +3,8 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\Nasa\V1\SettingNavController as NasaV1SettingNavController;
use App\Http\Controllers\Api\Nasa\V1\TestsFnSubfileController as NasaV1TestsFnSubfileController;
use App\Http\Controllers\Api\Nasa\V1\FnCrawController as NasaV1FnCrawController;
Route::prefix('nasa/v1')->middleware('nasa.auth')->group(function () {
@ -18,4 +20,28 @@ Route::prefix('nasa/v1')->middleware('nasa.auth')->group(function () {
Route::get('nav/hit/{id}/{field}', [NasaV1SettingNavController::class, 'hit']);
});
Route::prefix('tests/fn/subfile')->group(function() {
Route::get('', [NasaV1TestsFnSubfileController::class, 'index']);
Route::get('detail/{id}', [NasaV1TestsFnSubfileController::class, 'detail']);
Route::put('detail/save/{id}', [NasaV1TestsFnSubfileController::class, 'detailSave']);
Route::delete('delete/{id}', [NasaV1TestsFnSubfileController::class, 'delete']);
Route::get('add', [NasaV1TestsFnSubfileController::class, 'add']);
Route::put('add/save', [NasaV1TestsFnSubfileController::class, 'addSave']);
Route::get('clone/{id}', [NasaV1TestsFnSubfileController::class, 'clone']);
Route::put('clone/save', [NasaV1TestsFnSubfileController::class, 'cloneSave']);
Route::get('hit/{id}/{field}', [NasaV1TestsFnSubfileController::class, 'hit']);
});
Route::prefix('fn/craw')->group(function() {
Route::get('', [NasaV1FnCrawController::class, 'index']);
Route::get('detail/{id}', [NasaV1FnCrawController::class, 'detail']);
Route::put('detail/save/{id}', [NasaV1FnCrawController::class, 'detailSave']);
Route::delete('delete/{id}', [NasaV1FnCrawController::class, 'delete']);
Route::get('add', [NasaV1FnCrawController::class, 'add']);
Route::put('add/save', [NasaV1FnCrawController::class, 'addSave']);
Route::get('clone/{id}', [NasaV1FnCrawController::class, 'clone']);
Route::put('clone/save', [NasaV1FnCrawController::class, 'cloneSave']);
Route::get('hit/{id}/{field}', [NasaV1FnCrawController::class, 'hit']);
});
});

View File

@ -28,10 +28,14 @@ use App\Http\Controllers\Web\Nasa\V1\Demo\NasaStyleController as NasaDemoNasaSty
use App\Http\Controllers\Web\Nasa\V1\Base\ListController as NasaListController;
use App\Http\Controllers\Web\Nasa\V1\AuthController;
use App\Http\Controllers\Web\Nasa\V1\MasterPanelNavController as NasaMasterPanelNavController;
use App\Http\Controllers\Web\Nasa\V1\MasterFnCrawController as NasaMasterFnCrawController;
use App\Http\Controllers\Web\Nasa\V1\FuNodeBaseController as NasaFuNodeBaseController;
use App\Http\Controllers\Web\Nasa\V1\FuNodeActionController as NasaFuNodeActionController;
use App\Http\Controllers\Web\Nasa\V1\FuEnvConfigController as NasaFuEnvConfigController;
use App\Http\Controllers\Web\Nasa\V1\FuMailMustlogController as NasaFuMailMustlogController;
use App\Http\Controllers\Web\Nasa\V1\TestsFnSubfileController as NasaTestsFnSubfileController;
use App\Http\Controllers\Web\Nasa\V1\FuAnnBaseController as NasaFuAnnBaseController;
use App\Http\Controllers\Web\Nasa\V1\BookController as NasaBookController;
Route::get('/birds/create', [BirdController::class, 'create'])->name('birds.create'); // 录入界面
Route::get('/birds/{id}', [BirdController::class, 'show'])->name('birds.show'); // 详情展示界面
@ -44,6 +48,9 @@ Route::post('/nasa/auth/in', [AuthController::class, 'in']);
Route::prefix('nasa')->middleware(['auth'])->group(function () {
Route::get('book/{id}', [NasaBookController::class, 'find']);
Route::put('book/{id}', [NasaBookController::class, 'save']);
Route::get('list', [NasaListController::class, 'index']);
Route::get('list/detail/{id}', [NasaListController::class, 'detail']);
Route::put('list/detail/{id}', [NasaListController::class, 'detailSave']);
@ -63,10 +70,13 @@ Route::prefix('nasa')->middleware(['auth'])->group(function () {
});
Route::get('/master/panel/nav', [NasaMasterPanelNavController::class, 'index'])->name('nasa.master.panel.nav');
Route::get('/master/fn/craw', [NasaMasterFnCrawController::class, 'index'])->name('nasa.master.fn.craw');
Route::get('/fu/node/base', [NasaFuNodeBaseController::class, 'index'])->name('nasa.fu.node.base');
Route::get('/fu/node/action/fly', [NasaFuNodeActionController::class, 'fly']);
Route::get('/fu/env/config', [NasaFuEnvConfigController::class, 'index'])->name('nasa.fu.env.config');
Route::get('/fu/mail/mustlog', [NasaFuMailMustlogController::class, 'index'])->name('nasa.fu.mail.mustlog');
Route::get('/fu/ann/base', [NasaFuAnnBaseController::class, 'index'])->name('nasa.fu.ann.base');
Route::get('/tests/fn/subfile', [NasaTestsFnSubfileController::class, 'index'])->name('nasa.tests.fn.subfile');
Route::get('demo')->name('nasa.demo');
Route::prefix('demo')->group(function() {