mirror of
https://github.com/kuaifan/dootask.git
synced 2026-06-29 10:42:19 +00:00
chore(quality): 引入 phpstan/ESLint/CI 门禁、Claude hooks 与代码检索地图
- phpstan(larastan ^3.10, level 1 + baseline 封存 86 个存量错误),composer stan / stan-baseline - ESLint 9 flat config(vue2-essential,存量违规降 warn,error 基线为 0),npm run lint - CI:.github/workflows/tests.yml(static-checks + phpunit,phpunit 用 kuaifan/php 镜像跑,FFI doo.so 不在仓库) - Claude Code hooks:编辑 app/ 下 PHP 后自动单文件 phpstan,失败回灌 - 检索地图:routes/api-map.md(doc:api-map 生成,325 接口)、docs/events-map.md(events:map)、types/dootask-globals.d.ts($A 207 方法)、npm run check:lang(存量缺失 93 条,CI 暂非阻塞) - CLAUDE.md:版本号更正 Laravel 13/PHP 8.4,新增质量门禁、检索地图、架构增量规则章节 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8fb6d331f8
commit
383664aef7
53
.claude/hooks/php-stan-check.sh
Executable file
53
.claude/hooks/php-stan-check.sh
Executable file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
# Claude Code PostToolUse hook:Edit/Write 改动 app/ 下的 PHP 文件后,自动在 PHP 容器内
|
||||
# 对该文件跑 phpstan 单文件分析。失败时 exit 2,把错误回灌给 Claude 修复。
|
||||
# 任何环境不满足(无 python3 / 容器未运行 / 未装 phpstan)都静默放行,绝不阻塞编辑。
|
||||
set -u
|
||||
|
||||
INPUT=$(cat)
|
||||
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
||||
|
||||
command -v python3 >/dev/null 2>&1 || exit 0
|
||||
command -v docker >/dev/null 2>&1 || exit 0
|
||||
|
||||
FILE_PATH=$(printf '%s' "$INPUT" | python3 -c "import sys,json;print(json.load(sys.stdin).get('tool_input',{}).get('file_path',''))" 2>/dev/null) || exit 0
|
||||
[ -n "$FILE_PATH" ] || exit 0
|
||||
|
||||
case "$FILE_PATH" in
|
||||
*.php) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
REL_PATH="${FILE_PATH#"$PROJECT_DIR"/}"
|
||||
case "$REL_PATH" in
|
||||
app/*) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
[ -f "$PROJECT_DIR/$REL_PATH" ] || exit 0
|
||||
|
||||
# 定位挂载本项目的 PHP 容器:
|
||||
# ① 环境变量 DOOTASK_PHP_CONTAINER;② .env 的 APP_ID;③ 扫描 /var/www 挂载源为本项目的容器
|
||||
CONTAINER="${DOOTASK_PHP_CONTAINER:-}"
|
||||
if [ -z "$CONTAINER" ] && [ -f "$PROJECT_DIR/.env" ]; then
|
||||
APP_ID=$(grep -E '^APP_ID=' "$PROJECT_DIR/.env" 2>/dev/null | head -1 | cut -d= -f2 | tr -d '"' | tr -d "'")
|
||||
if [ -n "$APP_ID" ] && docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "dootask-php-$APP_ID"; then
|
||||
CONTAINER="dootask-php-$APP_ID"
|
||||
fi
|
||||
fi
|
||||
if [ -z "$CONTAINER" ]; then
|
||||
RUNNING=$(docker ps -q 2>/dev/null)
|
||||
[ -n "$RUNNING" ] && CONTAINER=$(docker inspect --format '{{.Name}}|{{range .Mounts}}{{if eq .Destination "/var/www"}}{{.Source}}{{end}}{{end}}' $RUNNING 2>/dev/null \
|
||||
| awk -F'|' -v dir="$PROJECT_DIR" '$2 == dir {gsub(/^\//, "", $1); print $1; exit}')
|
||||
fi
|
||||
[ -n "$CONTAINER" ] || exit 0
|
||||
docker exec "$CONTAINER" test -f /var/www/vendor/bin/phpstan 2>/dev/null || exit 0
|
||||
|
||||
OUTPUT=$(docker exec "$CONTAINER" sh -c "cd /var/www && php vendor/bin/phpstan analyse --no-progress --error-format=raw --memory-limit=-1 '$REL_PATH'" 2>&1)
|
||||
if [ $? -ne 0 ]; then
|
||||
{
|
||||
echo "phpstan 检查未通过($REL_PATH),请修复以下问题:"
|
||||
printf '%s\n' "$OUTPUT" | grep -v '^Note:' | tail -30
|
||||
} >&2
|
||||
exit 2
|
||||
fi
|
||||
exit 0
|
||||
16
.claude/settings.json
Normal file
16
.claude/settings.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/php-stan-check.sh",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
81
.github/workflows/tests.yml
vendored
Normal file
81
.github/workflows/tests.yml
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [pro, master]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
static-checks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.4'
|
||||
extensions: mbstring, intl, gd, xml, zip, swoole, redis
|
||||
tools: composer:v2
|
||||
|
||||
- name: Install Composer Dependencies
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: PHPStan
|
||||
run: composer stan
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: npm install --no-audit --no-fund
|
||||
|
||||
- name: ESLint
|
||||
run: npm run lint
|
||||
|
||||
# 存量缺失文案 93 条(见 scripts/check-language.mjs 输出),清零后移除 continue-on-error 改为强制
|
||||
- name: Language Check
|
||||
run: npm run check:lang
|
||||
continue-on-error: true
|
||||
|
||||
phpunit:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mariadb:
|
||||
image: mariadb:10.7.3
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: test
|
||||
MYSQL_DATABASE: dootask
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: >-
|
||||
--health-cmd="mysqladmin ping -h localhost -ptest"
|
||||
--health-interval=10s
|
||||
--health-timeout=5s
|
||||
--health-retries=10
|
||||
redis:
|
||||
image: redis:alpine
|
||||
ports:
|
||||
- 6379:6379
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# 用项目自身的 PHP 镜像跑测试(内含 /usr/lib/doo/doo.so FFI 库,裸 runner 无法运行)
|
||||
- name: Run PHPUnit in DooTask PHP image
|
||||
run: |
|
||||
docker run --rm --network host -v "$GITHUB_WORKSPACE":/var/www -w /var/www \
|
||||
kuaifan/php:swoole-8.4 sh -c '
|
||||
composer install --prefer-dist --no-interaction &&
|
||||
cp .env.example .env &&
|
||||
sed -i "s/^DB_HOST=.*/DB_HOST=127.0.0.1/" .env &&
|
||||
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=dootask/" .env &&
|
||||
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=test/" .env &&
|
||||
sed -i "s/^REDIS_HOST=.*/REDIS_HOST=127.0.0.1/" .env &&
|
||||
php artisan key:generate &&
|
||||
php artisan migrate --seed --force &&
|
||||
php vendor/bin/phpunit
|
||||
'
|
||||
22
CLAUDE.md
22
CLAUDE.md
@ -1,6 +1,6 @@
|
||||
## 项目概述
|
||||
|
||||
Laravel 8 (LaravelS/Swoole) + Vue 2 (Vite) + Electron。开源任务/项目管理系统。
|
||||
Laravel 13 (LaravelS/Swoole, PHP 8.4) + Vue 2 (Vite) + Electron。开源任务/项目管理系统。
|
||||
|
||||
## 开发命令
|
||||
|
||||
@ -17,11 +17,31 @@ Laravel 8 (LaravelS/Swoole) + Vue 2 (Vite) + Electron。开源任务/项目管
|
||||
|
||||
前端代码改动只做 Edit/Write,不要为了"验证"启动 dev server。用户明确说"跑一下 / 出包"时除外。
|
||||
|
||||
### 质量门禁(改完代码必须自查,CI 同步在跑,见 .github/workflows/tests.yml)
|
||||
|
||||
- `./cmd composer stan` — phpstan(level 1 + baseline,存量已封存,新增错误必须清零)
|
||||
- `npm run lint` — ESLint(error 必须为 0;warn 是存量遗留,见 eslint.config.mjs 注释)
|
||||
- `npm run check:lang` — 校验前端 `$L()` 字面量是否已登记到 `language/original-web.txt`
|
||||
- 改动控制器 public 方法或路由后跑 `./cmd artisan doc:api-map` 重新生成对照表
|
||||
|
||||
## 代码检索地图(先查表,再 grep)
|
||||
|
||||
- API URL ↔ 控制器方法对照:`routes/api-map.md`(生成式文件,勿手改)
|
||||
- 前端事件总线(mitt)收发对照:`docs/events-map.md`(`npm run events:map` 重新生成)
|
||||
- `$A` / `$L` 全局工具类型声明:`types/dootask-globals.d.ts`(新增 `$A` 方法须同步此文件)
|
||||
|
||||
## 架构增量规则(只约束新增代码,存量"动到哪迁到哪")
|
||||
|
||||
- **巨型文件冻结**:不再往 `ProjectController`、`UsersController`、`DialogController`、`app/Module/Base.php`、`resources/assets/js/store/actions.js` 新增方法/函数;新功能领域开新控制器或新模块文件(动态路由天然支持多控制器)
|
||||
- **业务编排归层**:跨模型的业务流程写在 `app/Module/`(或 `app/Services/`),模型只保留数据访问与自身状态变更;Swoole Task 只做投递与调用,不直接编排业务
|
||||
- **配置读取**:业务代码禁止直接 `env()`,统一走 `config()`(项目自有配置集中在 `config/dootask.php`)
|
||||
|
||||
## Gotchas
|
||||
|
||||
### LaravelS/Swoole
|
||||
|
||||
- **避免在静态属性、单例、全局变量中存储请求级状态**——请求间共享进程,会导致数据串联和内存泄漏
|
||||
- 要存请求级状态,用 `RequestContext::save('key', $value)` / `RequestContext::get('key')`(参考 `User::authInfo()` 的用法,见 `app/Services/RequestContext.php`)
|
||||
- 构造函数、服务提供者、`boot()` 方法不会在每个请求重新执行
|
||||
- 配置/路由变更需要 `./cmd php restart` 或容器重启才能生效
|
||||
- 长生命周期逻辑(WebSocket、定时器)应复用现有模式,避免阻塞协程/事件循环
|
||||
|
||||
143
app/Console/Commands/DocApiMap.php
Normal file
143
app/Console/Commands/DocApiMap.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
|
||||
class DocApiMap extends Command
|
||||
{
|
||||
protected $signature = 'doc:api-map';
|
||||
protected $description = '生成 API 路由对照表(routes/api-map.md)';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$controllers = $this->collectControllers();
|
||||
if (empty($controllers)) {
|
||||
$this->error('未从路由中解析到任何 api 控制器');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
$sections = [];
|
||||
foreach ($controllers as $prefix => $class) {
|
||||
$rows = $this->collectMethods($prefix, $class);
|
||||
$total += count($rows);
|
||||
$sections[] = $this->renderSection($prefix, $class, $rows);
|
||||
}
|
||||
|
||||
$path = base_path('routes/api-map.md');
|
||||
file_put_contents($path, $this->renderHeader($total) . implode("\n", $sections));
|
||||
|
||||
$this->info("已生成: routes/api-map.md(控制器 " . count($controllers) . " 个,接口 {$total} 个)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从已注册路由中收集 api 前缀与控制器的映射
|
||||
* 匹配 routes/web.php 中的动态路由:api/{prefix}/{method}
|
||||
* @return array [prefix => 控制器类名]
|
||||
*/
|
||||
private function collectControllers(): array
|
||||
{
|
||||
$controllers = [];
|
||||
foreach (Route::getRoutes() as $route) {
|
||||
if (!preg_match('/^api\/(\w+)\/\{method}$/', $route->uri())) {
|
||||
continue;
|
||||
}
|
||||
preg_match('/^api\/(\w+)\/\{method}$/', $route->uri(), $match);
|
||||
$class = $route->getAction('controller');
|
||||
if ($class && class_exists($class)) {
|
||||
$controllers[$match[1]] = $class;
|
||||
}
|
||||
}
|
||||
return $controllers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射收集控制器的接口方法
|
||||
* @param string $prefix 路由前缀(如 project)
|
||||
* @param string $class 控制器类名
|
||||
* @return array [['url' => ..., 'method' => ..., 'http' => ..., 'title' => ...], ...]
|
||||
*/
|
||||
private function collectMethods(string $prefix, string $class): array
|
||||
{
|
||||
$rows = [];
|
||||
$reflection = new ReflectionClass($class);
|
||||
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
// 仅保留本类声明的实例方法,排除 __invoke/__before/__construct 等魔术/框架方法
|
||||
if ($method->getDeclaringClass()->getName() !== $class
|
||||
|| $method->isStatic()
|
||||
|| str_starts_with($method->getName(), '__')) {
|
||||
continue;
|
||||
}
|
||||
[$http, $title] = $this->parseApiDoc($method);
|
||||
$rows[] = [
|
||||
'url' => "api/{$prefix}/" . str_replace('__', '/', $method->getName()),
|
||||
'method' => $method->getName() . '()',
|
||||
'http' => $http,
|
||||
'title' => $title,
|
||||
];
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析方法 docblock 中的 @api 注释行
|
||||
* 格式如:@api {get} api/project/lists 获取项目列表
|
||||
* @return array [HTTP 方法, 标题],无 @api 注释时为 ['any', '']
|
||||
*/
|
||||
private function parseApiDoc(ReflectionMethod $method): array
|
||||
{
|
||||
$doc = $method->getDocComment();
|
||||
if ($doc && preg_match('/@api\s+\{(\w+)}\s+(\S+)(?:[ \t]+(.+))?/', $doc, $match)) {
|
||||
return [strtolower($match[1]), trim($match[3] ?? '')];
|
||||
}
|
||||
return ['any', ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件头说明
|
||||
*/
|
||||
private function renderHeader(int $total): string
|
||||
{
|
||||
return <<<MD
|
||||
# API 路由对照表
|
||||
|
||||
> 此文件由 `php artisan doc:api-map` 生成,勿手改。
|
||||
|
||||
接口总数:{$total}
|
||||
|
||||
## 路由规则
|
||||
|
||||
API 使用动态路由(见 `routes/web.php`),URL 段映射为控制器方法名:
|
||||
|
||||
- `api/{controller}/{method}` → `{method}()`,如 `api/project/lists` → `ProjectController::lists()`
|
||||
- `api/{controller}/{method}/{action}` → `{method}__{action}()`(双下划线连接),如 `api/project/invite/join` → `ProjectController::invite__join()`
|
||||
- 路由最多两段,方法名最多一个双下划线
|
||||
|
||||
|
||||
MD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成单个控制器的表格段落
|
||||
*/
|
||||
private function renderSection(string $prefix, string $class, array $rows): string
|
||||
{
|
||||
$short = class_basename($class);
|
||||
$lines = [
|
||||
"## {$prefix}({$short})",
|
||||
'',
|
||||
'| URL | 方法名 | HTTP | 说明 |',
|
||||
'| --- | --- | --- | --- |',
|
||||
];
|
||||
foreach ($rows as $row) {
|
||||
$lines[] = "| {$row['url']} | {$row['method']} | {$row['http']} | {$row['title']} |";
|
||||
}
|
||||
$lines[] = '';
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
}
|
||||
@ -46,6 +46,7 @@
|
||||
"fakerphp/faker": "^1.24",
|
||||
"hhxsv5/laravel-s": "~3.8.0",
|
||||
"kitloong/laravel-migrations-generator": "^7.4",
|
||||
"larastan/larastan": "^3.10",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.6",
|
||||
"phpunit/phpunit": "^11.5"
|
||||
@ -75,7 +76,9 @@
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi"
|
||||
]
|
||||
],
|
||||
"stan": "phpstan analyse --no-progress --memory-limit=-1",
|
||||
"stan-baseline": "phpstan analyse --generate-baseline --memory-limit=-1"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
|
||||
203
composer.lock
generated
203
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "27bf8f16ef5c8e3d75a68a7b666531b6",
|
||||
"content-hash": "ed96e1ded2a0dd87cdc42fc951ea848c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
@ -8732,6 +8732,47 @@
|
||||
],
|
||||
"time": "2026-01-17T09:14:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "iamcal/sql-parser",
|
||||
"version": "v0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/iamcal/SQLParser.git",
|
||||
"reference": "610392f38de49a44dab08dc1659960a29874c4b8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/iamcal/SQLParser/zipball/610392f38de49a44dab08dc1659960a29874c4b8",
|
||||
"reference": "610392f38de49a44dab08dc1659960a29874c4b8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^1.0",
|
||||
"phpunit/phpunit": "^5|^6|^7|^8|^9"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"iamcal\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Cal Henderson",
|
||||
"email": "cal@iamcal.com"
|
||||
}
|
||||
],
|
||||
"description": "MySQL schema parser",
|
||||
"support": {
|
||||
"issues": "https://github.com/iamcal/SQLParser/issues",
|
||||
"source": "https://github.com/iamcal/SQLParser/tree/v0.7"
|
||||
},
|
||||
"time": "2026-01-28T22:20:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "kitloong/laravel-migrations-generator",
|
||||
"version": "v7.4.0",
|
||||
@ -8810,6 +8851,96 @@
|
||||
],
|
||||
"time": "2026-05-10T14:54:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "larastan/larastan",
|
||||
"version": "v3.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/larastan/larastan.git",
|
||||
"reference": "2970f83398154178a739609c244577267c7ee8eb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/larastan/larastan/zipball/2970f83398154178a739609c244577267c7ee8eb",
|
||||
"reference": "2970f83398154178a739609c244577267c7ee8eb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"iamcal/sql-parser": "^0.7.0",
|
||||
"illuminate/console": "^11.44.2 || ^12.4.1 || ^13",
|
||||
"illuminate/container": "^11.44.2 || ^12.4.1 || ^13",
|
||||
"illuminate/contracts": "^11.44.2 || ^12.4.1 || ^13",
|
||||
"illuminate/database": "^11.44.2 || ^12.4.1 || ^13",
|
||||
"illuminate/http": "^11.44.2 || ^12.4.1 || ^13",
|
||||
"illuminate/pipeline": "^11.44.2 || ^12.4.1 || ^13",
|
||||
"illuminate/support": "^11.44.2 || ^12.4.1 || ^13",
|
||||
"php": "^8.2",
|
||||
"phpstan/phpstan": "^2.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^14",
|
||||
"laravel/framework": "^11.44.2 || ^12.7.2 || ^13",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"nikic/php-parser": "^5.4",
|
||||
"orchestra/canvas": "^v9.2.2 || ^10.0.1 || ^11",
|
||||
"orchestra/testbench-core": "^9.12.0 || ^10.1 || ^11",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.1",
|
||||
"phpunit/phpunit": "^10.5.35 || ^11.5.15 || ^12.5.8 || ^13.1.8"
|
||||
},
|
||||
"suggest": {
|
||||
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
|
||||
"phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically"
|
||||
},
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Larastan\\Larastan\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Can Vural",
|
||||
"email": "can9119@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel",
|
||||
"keywords": [
|
||||
"PHPStan",
|
||||
"code analyse",
|
||||
"code analysis",
|
||||
"larastan",
|
||||
"laravel",
|
||||
"package",
|
||||
"php",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/larastan/larastan/issues",
|
||||
"source": "https://github.com/larastan/larastan/tree/v3.10.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/canvural",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-05-28T08:00:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
"version": "1.6.12",
|
||||
@ -9167,6 +9298,70 @@
|
||||
},
|
||||
"time": "2022-02-21T01:04:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.2.2",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6",
|
||||
"reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
},
|
||||
"bin": [
|
||||
"phpstan",
|
||||
"phpstan.phar"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ondřej Mirtes"
|
||||
},
|
||||
{
|
||||
"name": "Markus Staab"
|
||||
},
|
||||
{
|
||||
"name": "Vincent Langlet"
|
||||
}
|
||||
],
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||
"source": "https://github.com/phpstan/phpstan-src"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/ondrejmirtes",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/phpstan",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-06-05T09:00:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "11.0.12",
|
||||
@ -10747,7 +10942,7 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
@ -10764,6 +10959,6 @@
|
||||
"ext-simplexml": "*",
|
||||
"ext-zip": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.9.0"
|
||||
}
|
||||
|
||||
323
docs/events-map.md
Normal file
323
docs/events-map.md
Normal file
@ -0,0 +1,323 @@
|
||||
# 前端事件总线注册表
|
||||
|
||||
> **本文件由脚本自动生成,请勿手改。**
|
||||
>
|
||||
> - 生成命令: `node scripts/gen-events-map.mjs`
|
||||
> - 扫描范围: `resources/assets/js` 下所有 `.js` / `.vue` 文件(共 268 个)
|
||||
> - 事件总线: `resources/assets/js/store/events.js`(mitt 实例)
|
||||
> - 仅匹配裸 `emitter.emit/on/off(` 调用;`xxx.emitter.emit(`(如 Quill 内部 emitter)不属于本总线,已排除
|
||||
|
||||
共 **29** 个静态可解析事件,**125** 处 `emitter.emit/on/off` 调用。
|
||||
|
||||
## 事件清单
|
||||
|
||||
### `addMeeting`
|
||||
|
||||
- **emit(10)**
|
||||
- `resources/assets/js/App.vue:420`
|
||||
- `resources/assets/js/pages/manage.vue:1236`
|
||||
- `resources/assets/js/pages/manage.vue:1243`
|
||||
- `resources/assets/js/pages/manage/application.vue:1188`
|
||||
- `resources/assets/js/pages/manage/application.vue:1194`
|
||||
- `resources/assets/js/pages/manage/components/ChatInput/index.vue:1882`
|
||||
- `resources/assets/js/pages/manage/components/DialogView/index.vue:621`
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:2017`
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:2025`
|
||||
- `resources/assets/js/pages/manage/messenger.vue:1219`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage/components/MeetingManager/index.vue:187`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage/components/MeetingManager/index.vue:191`
|
||||
|
||||
### `addTask`
|
||||
|
||||
- **emit(3)**
|
||||
- `resources/assets/js/pages/manage/calendar.vue:255`
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:3513`
|
||||
- `resources/assets/js/pages/manage/components/ProjectPanel.vue:1357`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage.vue:621`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage.vue:641`
|
||||
|
||||
### `aiAssistantClosed`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/components/AIAssistant/index.vue:420`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/AIAssistant/float-button.vue:154`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/AIAssistant/float-button.vue:162`
|
||||
|
||||
### `aiOperationRequest`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/store/actions.js:4781`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/AIAssistant/float-button.vue:155`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/AIAssistant/float-button.vue:163`
|
||||
|
||||
### `approveDetails`
|
||||
|
||||
- **emit(2)**
|
||||
- `resources/assets/js/pages/manage/approve/index.vue:497`
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:3826`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage.vue:624`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage.vue:644`
|
||||
|
||||
### `clickAgainDialog`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/components/Mobile/Tabbar.vue:182`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage/messenger.vue:344`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage/messenger.vue:348`
|
||||
|
||||
### `createGroup`
|
||||
|
||||
- **emit(3)**
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:2871`
|
||||
- `resources/assets/js/pages/manage/components/UserDetail.vue:288`
|
||||
- `resources/assets/js/pages/manage/messenger.vue:1224`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage.vue:622`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage.vue:642`
|
||||
|
||||
### `dialogMsgPush`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/store/actions.js:4852`
|
||||
- **on(2)**
|
||||
- `resources/assets/js/components/Mobile/Tabbar.vue:49`
|
||||
- `resources/assets/js/pages/manage.vue:623`
|
||||
- **off(2)**
|
||||
- `resources/assets/js/components/Mobile/Tabbar.vue:53`
|
||||
- `resources/assets/js/pages/manage.vue:643`
|
||||
|
||||
### `handleMoveTop`
|
||||
|
||||
- **emit(2)**
|
||||
- `resources/assets/js/store/actions.js:2727`
|
||||
- `resources/assets/js/store/actions.js:3720`
|
||||
- **on(2)**
|
||||
- `resources/assets/js/pages/manage/components/DialogModal.vue:41`
|
||||
- `resources/assets/js/pages/manage/components/TaskModal.vue:49`
|
||||
- **off(2)**
|
||||
- `resources/assets/js/pages/manage/components/DialogModal.vue:45`
|
||||
- `resources/assets/js/pages/manage/components/TaskModal.vue:53`
|
||||
|
||||
### `observeMicroApp:open`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/store/actions.js:5361`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/MicroApps/index.vue:144`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/MicroApps/index.vue:149`
|
||||
|
||||
### `observeMicroApp:updatedOrUninstalled`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/store/mutations.js:429`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/MicroApps/index.vue:145`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/MicroApps/index.vue:150`
|
||||
|
||||
### `openAIAssistant`
|
||||
|
||||
- **emit(7)**
|
||||
- `resources/assets/js/components/AIAssistant/float-button.vue:476`
|
||||
- `resources/assets/js/components/SearchBox.vue:582`
|
||||
- `resources/assets/js/pages/manage.vue:1267`
|
||||
- `resources/assets/js/pages/manage/components/ChatInput/index.vue:1925`
|
||||
- `resources/assets/js/pages/manage/components/ReportDetail.vue:176`
|
||||
- `resources/assets/js/pages/manage/components/ReportEdit.vue:267`
|
||||
- `resources/assets/js/pages/manage/components/TaskAdd.vue:703`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/AIAssistant/index.vue:361`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/AIAssistant/index.vue:368`
|
||||
|
||||
### `openAIAssistantGlobal`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/pages/manage.vue:1255`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/AIAssistant/float-button.vue:153`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/AIAssistant/float-button.vue:161`
|
||||
|
||||
### `openDownloadClient`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/pages/manage.vue:1128`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/RightBottom.vue:73`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/RightBottom.vue:78`
|
||||
|
||||
### `openFavorite`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/pages/manage/application.vue:1061`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage.vue:626`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage.vue:646`
|
||||
|
||||
### `openManageExport`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/pages/manage/application.vue:1108`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage.vue:628`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage.vue:648`
|
||||
|
||||
### `openMobileNotification`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/pages/manage.vue:1641`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/Mobile/Notification.vue:38`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/Mobile/Notification.vue:42`
|
||||
|
||||
### `openProjectInvite`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/App.vue:432`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage/components/ProjectInvite.vue:83`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage/components/ProjectInvite.vue:87`
|
||||
|
||||
### `openRecent`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/pages/manage/application.vue:1064`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage.vue:627`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage.vue:647`
|
||||
|
||||
### `openReport`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/pages/manage/application.vue:1058`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage.vue:625`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage.vue:645`
|
||||
|
||||
### `openSearch`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/pages/manage/dashboard.vue:256`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/SearchBox.vue:128`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/SearchBox.vue:132`
|
||||
|
||||
### `openUser`
|
||||
|
||||
- **emit(5)**
|
||||
- `resources/assets/js/components/UserAvatar/index.vue:184`
|
||||
- `resources/assets/js/pages/manage/approve/details.vue:546`
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:2940`
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:4494`
|
||||
- `resources/assets/js/pages/manage/messenger.vue:1229`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage/components/UserDetail.vue:166`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage/components/UserDetail.vue:170`
|
||||
|
||||
### `receiveTask`
|
||||
|
||||
- **emit(2)**
|
||||
- `resources/assets/js/pages/manage/components/ProjectPanel.vue:1768`
|
||||
- `resources/assets/js/pages/manage/components/TaskRow.vue:280`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage/components/TaskDetail.vue:738`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage/components/TaskDetail.vue:745`
|
||||
|
||||
### `streamMsgData`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/store/actions.js:4469`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:946`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:956`
|
||||
|
||||
### `taskRelationUpdate`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/store/actions.js:4992`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/manage/components/TaskDetail.vue:739`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/manage/components/TaskDetail.vue:746`
|
||||
|
||||
### `updateNotification`
|
||||
|
||||
- **emit(2)**
|
||||
- `resources/assets/js/pages/manage.vue:1125`
|
||||
- `resources/assets/js/pages/manage/setting/index.vue:191`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/RightBottom.vue:65`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/RightBottom.vue:77`
|
||||
|
||||
### `useSSOLogin`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/components/RightBottom.vue:231`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/pages/login.vue:217`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/pages/login.vue:222`
|
||||
|
||||
### `userActive`
|
||||
|
||||
- **emit(3)**
|
||||
- `resources/assets/js/store/actions.js:870`
|
||||
- `resources/assets/js/store/actions.js:952`
|
||||
- `resources/assets/js/store/actions.js:4769`
|
||||
- **on(1)**
|
||||
- `resources/assets/js/components/UserAvatar/index.vue:43`
|
||||
- **off(1)**
|
||||
- `resources/assets/js/components/UserAvatar/index.vue:47`
|
||||
|
||||
### `websocketMsg`
|
||||
|
||||
- **emit(1)**
|
||||
- `resources/assets/js/store/actions.js:4786`
|
||||
- **on(3)**
|
||||
- `resources/assets/js/pages/manage/approve/index.vue:380`
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:945`
|
||||
- `resources/assets/js/pages/manage/components/FileContent.vue:202`
|
||||
- **off(3)**
|
||||
- `resources/assets/js/pages/manage/approve/index.vue:383`
|
||||
- `resources/assets/js/pages/manage/components/DialogWrapper.vue:957`
|
||||
- `resources/assets/js/pages/manage/components/FileContent.vue:225`
|
||||
|
||||
## 动态事件名(无法静态解析)
|
||||
|
||||
以下调用的第一参数不是字符串字面量,无法静态解析事件名:
|
||||
|
||||
- `resources/assets/js/components/MicroApps/index.vue:365` — `emitter.emit(actionName...)`
|
||||
|
||||
## 统计
|
||||
|
||||
- 事件总数(静态可解析): **29**
|
||||
- 只 emit 无 on(疑似死事件): **0**
|
||||
- 只 on 无 emit(无人发射): **0**
|
||||
- 动态事件名调用: **1**
|
||||
67
eslint.config.mjs
Normal file
67
eslint.config.mjs
Normal file
@ -0,0 +1,67 @@
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import globals from 'globals';
|
||||
|
||||
// 起步策略:只开能抓真实 bug 的规则,存量代码必须 0 error;
|
||||
// 风格类/噪声规则后续按需逐步收紧。
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
'node_modules/**',
|
||||
'vendor/**',
|
||||
'public/**',
|
||||
'electron/**',
|
||||
'docker/**',
|
||||
'resources/assets/statics/**',
|
||||
// 第三方移植的 directive,内含过期的 babel/* eslint 注释
|
||||
'resources/assets/js/directives/v-click-outside-x.js',
|
||||
],
|
||||
},
|
||||
...pluginVue.configs['flat/vue2-essential'],
|
||||
{
|
||||
files: ['resources/assets/js/**/*.{js,mjs,vue}', 'scripts/**/*.mjs'],
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: 'off',
|
||||
},
|
||||
languageOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
$A: 'readonly',
|
||||
$L: 'readonly',
|
||||
$: 'readonly',
|
||||
jQuery: 'readonly',
|
||||
LANGUAGE_DATA: 'readonly',
|
||||
SystemConfig: 'readonly',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-dupe-keys': 'error',
|
||||
'no-dupe-args': 'error',
|
||||
'no-const-assign': 'error',
|
||||
'no-class-assign': 'error',
|
||||
'no-compare-neg-zero': 'error',
|
||||
'no-self-assign': 'error',
|
||||
'use-isnan': 'error',
|
||||
'valid-typeof': 'error',
|
||||
// 以下规则存量代码有违规,先降为 warn 保持可见;清零后逐条升回 error
|
||||
'no-unreachable': 'warn',
|
||||
'no-cond-assign': 'warn',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/require-v-for-key': 'warn',
|
||||
'vue/no-use-v-if-with-v-for': 'warn',
|
||||
'vue/valid-template-root': 'warn',
|
||||
'vue/valid-v-for': 'warn',
|
||||
'vue/no-unused-components': 'warn',
|
||||
'vue/no-mutating-props': 'warn',
|
||||
'vue/no-unused-vars': 'warn',
|
||||
'vue/no-textarea-mustache': 'warn',
|
||||
'vue/no-reserved-keys': 'warn',
|
||||
'vue/no-side-effects-in-computed-properties': 'warn',
|
||||
'vue/no-v-text-v-html-on-component': 'warn',
|
||||
'vue/require-valid-default-prop': 'warn',
|
||||
'vue/valid-v-show': 'warn',
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -21,7 +21,7 @@
|
||||
"resources/assets/js/**/*",
|
||||
"resources/assets/**/*.vue",
|
||||
"resources/assets/sass/**/*",
|
||||
"types/**/*.d.ts"
|
||||
"types/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
||||
11
package.json
11
package.json
@ -5,7 +5,10 @@
|
||||
"description": "DooTask is task management system.",
|
||||
"scripts": {
|
||||
"start": "./cmd dev",
|
||||
"build": "./cmd prod"
|
||||
"build": "./cmd prod",
|
||||
"lint": "eslint resources/assets/js",
|
||||
"check:lang": "node scripts/check-language.mjs",
|
||||
"events:map": "node scripts/gen-events-map.mjs"
|
||||
},
|
||||
"author": {
|
||||
"name": "KuaiFan",
|
||||
@ -32,7 +35,10 @@
|
||||
"dexie": "^3.2.3",
|
||||
"echarts": "^5.2.2",
|
||||
"element-sea": "^2.15.10-9",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-vue": "^10.9.2",
|
||||
"file-loader": "^6.2.0",
|
||||
"globals": "^17.6.0",
|
||||
"highlight.js": "^11.7.0",
|
||||
"html-to-md": "^0.8.8",
|
||||
"inquirer": "^8.2.0",
|
||||
@ -91,6 +97,5 @@
|
||||
"url": "https://www.dootask.com/api/download/update"
|
||||
}
|
||||
}
|
||||
],
|
||||
"dependencies": {}
|
||||
]
|
||||
}
|
||||
|
||||
349
phpstan-baseline.neon
Normal file
349
phpstan-baseline.neon
Normal file
@ -0,0 +1,349 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
-
|
||||
message: '#^Variable \$rules in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Exceptions/ImagePathHandler.php
|
||||
|
||||
-
|
||||
message: '#^Anonymous function has an unused use \$user\.$#'
|
||||
identifier: closure.unusedUse
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/ApproveController.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/ApproveController.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 2
|
||||
path: app/Http/Controllers/Api/DialogController.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$dialog in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/DialogController.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$user might not be defined\.$#'
|
||||
identifier: variable.undefined
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/DialogController.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/FileController.php
|
||||
|
||||
-
|
||||
message: '#^Anonymous function has an unused use \$user\.$#'
|
||||
identifier: closure.unusedUse
|
||||
count: 2
|
||||
path: app/Http/Controllers/Api/ProjectController.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$column in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/ProjectController.php
|
||||
|
||||
-
|
||||
message: '#^Relation ''sendUser'' is not found in App\\Models\\Report model\.$#'
|
||||
identifier: larastan.relationExistence
|
||||
count: 3
|
||||
path: app/Http/Controllers/Api/ReportController.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 14
|
||||
path: app/Http/Controllers/Api/SystemController.php
|
||||
|
||||
-
|
||||
message: '#^Anonymous function has an unused use \$keys\.$#'
|
||||
identifier: closure.unusedUse
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/UsersController.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/UsersController.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$basic in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Http/Controllers/Api/UsersController.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 1
|
||||
path: app/Http/Controllers/IndexController.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property App\\Ldap\\LdapUser\:\:\$displayName\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: app/Ldap/LdapUser.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property App\\Ldap\\LdapUser\:\:\$jpegPhoto\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: app/Ldap/LdapUser.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property App\\Ldap\\LdapUser\:\:\$uid\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: app/Ldap/LdapUser.php
|
||||
|
||||
-
|
||||
message: '#^Unsafe usage of new static\(\)\.$#'
|
||||
identifier: new.static
|
||||
count: 1
|
||||
path: app/Ldap/LdapUser.php
|
||||
|
||||
-
|
||||
message: '#^Unsafe usage of new static\(\)\.$#'
|
||||
identifier: new.static
|
||||
count: 3
|
||||
path: app/Models/AbstractModel.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$arr in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Models/File.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$dirRow in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Models/File.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$filePath might not be defined\.$#'
|
||||
identifier: variable.undefined
|
||||
count: 2
|
||||
path: app/Models/File.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$filePath in isset\(\) always exists and is not nullable\.$#'
|
||||
identifier: isset.variable
|
||||
count: 1
|
||||
path: app/Models/FileContent.php
|
||||
|
||||
-
|
||||
message: '#^Deprecated in PHP 8\.4\: Parameter \#3 \$task \(App\\Models\\ProjectTask\) is implicitly nullable via default value null\.$#'
|
||||
identifier: parameter.implicitlyNullable
|
||||
count: 1
|
||||
path: app/Models/ProjectPermission.php
|
||||
|
||||
-
|
||||
message: '#^Access to an undefined property App\\Models\\ProjectTask\:\:\$owner\.$#'
|
||||
identifier: property.notFound
|
||||
count: 1
|
||||
path: app/Models/ProjectTask.php
|
||||
|
||||
-
|
||||
message: '#^Class App\\Models\\ProjectFlowItem referenced with incorrect case\: App\\Models\\projectFlowItem\.$#'
|
||||
identifier: class.nameCase
|
||||
count: 1
|
||||
path: app/Models/ProjectTask.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$receivers in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Models/ProjectTask.php
|
||||
|
||||
-
|
||||
message: '#^Deprecated in PHP 8\.4\: Parameter \#3 \$time \(Carbon\\Carbon\) is implicitly nullable via default value null\.$#'
|
||||
identifier: parameter.implicitlyNullable
|
||||
count: 1
|
||||
path: app/Models/Report.php
|
||||
|
||||
-
|
||||
message: '#^Property ''receives'' does not exist in model\.$#'
|
||||
identifier: rules.modelAppends
|
||||
count: 1
|
||||
path: app/Models/Report.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 1
|
||||
path: app/Models/Setting.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 2
|
||||
path: app/Models/User.php
|
||||
|
||||
-
|
||||
message: '#^Anonymous function has an unused use \$originalUserid\.$#'
|
||||
identifier: closure.unusedUse
|
||||
count: 1
|
||||
path: app/Models/UserDepartment.php
|
||||
|
||||
-
|
||||
message: '#^Anonymous function has an unused use \$token\.$#'
|
||||
identifier: closure.unusedUse
|
||||
count: 1
|
||||
path: app/Models/UserDevice.php
|
||||
|
||||
-
|
||||
message: '#^Deprecated in PHP 8\.4\: Parameter \#1 \$token \(App\\Models\\UserDevice\|string\|int\) is implicitly nullable via default value null\.$#'
|
||||
identifier: parameter.implicitlyNullable
|
||||
count: 1
|
||||
path: app/Models/UserDevice.php
|
||||
|
||||
-
|
||||
message: '#^Deprecated in PHP 8\.4\: Parameter \#1 \$token \(string\) is implicitly nullable via default value null\.$#'
|
||||
identifier: parameter.implicitlyNullable
|
||||
count: 1
|
||||
path: app/Models/UserDevice.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$emailVerify in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Models/UserEmailVerification.php
|
||||
|
||||
-
|
||||
message: '#^Relation ''task'' is not found in App\\Models\\UserTaskBrowse model\.$#'
|
||||
identifier: larastan.relationExistence
|
||||
count: 1
|
||||
path: app/Models/UserTaskBrowse.php
|
||||
|
||||
-
|
||||
message: '#^Anonymous function has an unused use \$reserves\.$#'
|
||||
identifier: closure.unusedUse
|
||||
count: 2
|
||||
path: app/Models/WebSocketDialogMsg.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$embedding in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Module/AI.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$headers on left side of \?\? always exists and is not nullable\.$#'
|
||||
identifier: nullCoalesce.variable
|
||||
count: 1
|
||||
path: app/Module/AI.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$post on left side of \?\? always exists and is not nullable\.$#'
|
||||
identifier: nullCoalesce.variable
|
||||
count: 1
|
||||
path: app/Module/AI.php
|
||||
|
||||
-
|
||||
message: '#^Call to static method parseFile\(\) on an unknown class Symfony\\Component\\Yaml\\Yaml\.$#'
|
||||
identifier: class.notFound
|
||||
count: 1
|
||||
path: app/Module/Apps.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 1
|
||||
path: app/Module/Apps.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$deptIds in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Module/Apps.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$file on left side of \?\? always exists and is not nullable\.$#'
|
||||
identifier: nullCoalesce.variable
|
||||
count: 1
|
||||
path: app/Module/Base.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$url in empty\(\) always exists and is always falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Module/Base.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 1
|
||||
path: app/Module/Doo.php
|
||||
|
||||
-
|
||||
message: '#^Static method Redis\:\:eval\(\) invoked with 4 parameters, 1\-3 required\.$#'
|
||||
identifier: arguments.count
|
||||
count: 1
|
||||
path: app/Module/Lock.php
|
||||
|
||||
-
|
||||
message: '#^Static method Redis\:\:set\(\) invoked with 5 parameters, 2\-3 required\.$#'
|
||||
identifier: arguments.count
|
||||
count: 2
|
||||
path: app/Module/Lock.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 2
|
||||
path: app/Module/Manticore/ManticoreBase.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$idsToUpdate in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Module/Manticore/ManticoreBase.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$h might not be defined\.$#'
|
||||
identifier: variable.undefined
|
||||
count: 2
|
||||
path: app/Module/RandomColor.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$s might not be defined\.$#'
|
||||
identifier: variable.undefined
|
||||
count: 2
|
||||
path: app/Module/RandomColor.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$v might not be defined\.$#'
|
||||
identifier: variable.undefined
|
||||
count: 2
|
||||
path: app/Module/RandomColor.php
|
||||
|
||||
-
|
||||
message: '#^Unsafe usage of new static\(\)\.$#'
|
||||
identifier: new.static
|
||||
count: 1
|
||||
path: app/Module/Table/AbstractData.php
|
||||
|
||||
-
|
||||
message: '#^Variable \$strArr in empty\(\) always exists and is not falsy\.$#'
|
||||
identifier: empty.variable
|
||||
count: 1
|
||||
path: app/Module/Timer.php
|
||||
|
||||
-
|
||||
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
|
||||
identifier: larastan.noEnvCallsOutsideOfConfig
|
||||
count: 2
|
||||
path: app/Tasks/DeleteTmpTask.php
|
||||
12
phpstan.neon
Normal file
12
phpstan.neon
Normal file
@ -0,0 +1,12 @@
|
||||
includes:
|
||||
- vendor/larastan/larastan/extension.neon
|
||||
- phpstan-baseline.neon
|
||||
|
||||
parameters:
|
||||
level: 1
|
||||
paths:
|
||||
- app
|
||||
excludePaths:
|
||||
- app/Http/Controllers/Api/apidoc.php
|
||||
# 允许单文件分析(.claude hooks 用),baseline 中未命中的忽略项不报错
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
398
routes/api-map.md
Normal file
398
routes/api-map.md
Normal file
@ -0,0 +1,398 @@
|
||||
# API 路由对照表
|
||||
|
||||
> 此文件由 `php artisan doc:api-map` 生成,勿手改。
|
||||
|
||||
接口总数:325
|
||||
|
||||
## 路由规则
|
||||
|
||||
API 使用动态路由(见 `routes/web.php`),URL 段映射为控制器方法名:
|
||||
|
||||
- `api/{controller}/{method}` → `{method}()`,如 `api/project/lists` → `ProjectController::lists()`
|
||||
- `api/{controller}/{method}/{action}` → `{method}__{action}()`(双下划线连接),如 `api/project/invite/join` → `ProjectController::invite__join()`
|
||||
- 路由最多两段,方法名最多一个双下划线
|
||||
|
||||
## users(UsersController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/users/login | login() | get | 登录、注册 |
|
||||
| api/users/login/qrcode | login__qrcode() | get | 二维码登录 |
|
||||
| api/users/login/needcode | login__needcode() | get | 是否需要验证码 |
|
||||
| api/users/login/codeimg | login__codeimg() | get | 验证码图片 |
|
||||
| api/users/login/codejson | login__codejson() | get | 验证码json |
|
||||
| api/users/logout | logout() | get | 退出登录 |
|
||||
| api/users/token/expire | token__expire() | get | 查询 token 过期时间 |
|
||||
| api/users/reg/needinvite | reg__needinvite() | get | 是否需要邀请码 |
|
||||
| api/users/info | info() | get | 获取我的信息 |
|
||||
| api/users/info/managed_departments | info__managed_departments() | get | 获取我可切换负责人视角的部门列表 |
|
||||
| api/users/info/departments | info__departments() | get | 获取我的部门列表 |
|
||||
| api/users/editdata | editdata() | get | 修改自己的资料 |
|
||||
| api/users/editpass | editpass() | get | 修改自己的密码 |
|
||||
| api/users/search | search() | get | 搜索会员列表 |
|
||||
| api/users/search/ai | search__ai() | get | 获取AI机器人 |
|
||||
| api/users/basic | basic() | get | 获取指定会员基础信息 |
|
||||
| api/users/extra | extra() | get | 获取会员扩展信息 |
|
||||
| api/users/lists | lists() | get | 会员列表(限管理员) |
|
||||
| api/users/operation | operation() | get | 操作会员(限管理员) |
|
||||
| api/users/createuser | createuser() | post | 创建用户(管理员) |
|
||||
| api/users/import/preview | import__preview() | post | 批量导入预览(管理员) |
|
||||
| api/users/import | import() | post | 批量导入用户(管理员) |
|
||||
| api/users/import/template | import__template() | get | 下载批量导入模板(管理员) |
|
||||
| api/users/email/verification | email__verification() | get | 邮箱验证 |
|
||||
| api/users/umeng/alias | umeng__alias() | get | 设置友盟别名 |
|
||||
| api/users/meeting/open | meeting__open() | get | 【会议】创建会议、加入会议 |
|
||||
| api/users/tags/lists | tags__lists() | get | 获取个性标签列表 |
|
||||
| api/users/tags/add | tags__add() | post | 新增个性标签 |
|
||||
| api/users/tags/update | tags__update() | post | 修改个性标签 |
|
||||
| api/users/tags/delete | tags__delete() | post | 删除个性标签 |
|
||||
| api/users/tags/recognize | tags__recognize() | post | 认可个性标签 |
|
||||
| api/users/meeting/link | meeting__link() | get | 【会议】获取分享链接 |
|
||||
| api/users/meeting/tourist | meeting__tourist() | get | 【会议】游客信息 |
|
||||
| api/users/meeting/invitation | meeting__invitation() | get | 【会议】发送邀请 |
|
||||
| api/users/email/send | email__send() | get | 发送邮箱验证码 |
|
||||
| api/users/email/edit | email__edit() | get | 修改邮箱 |
|
||||
| api/users/delete/account | delete__account() | get | 删除帐号 |
|
||||
| api/users/department/list | department__list() | get | 部门列表(限管理员) |
|
||||
| api/users/department/add | department__add() | get | 新建、修改部门(限管理员) |
|
||||
| api/users/department/adddeputy | department__adddeputy() | post | 任命部门管理员(限管理员) |
|
||||
| api/users/department/deldeputy | department__deldeputy() | post | 罢免部门管理员(限管理员) |
|
||||
| api/users/department/del | department__del() | get | 删除部门(限管理员) |
|
||||
| api/users/department/sync | department__sync() | get | 同步部门成员(限管理员) |
|
||||
| api/users/checkin/get | checkin__get() | get | 获取签到设置 |
|
||||
| api/users/checkin/save | checkin__save() | post | 保存签到设置 |
|
||||
| api/users/checkin/list | checkin__list() | get | 获取签到数据 |
|
||||
| api/users/socket/status | socket__status() | get | 获取socket状态 |
|
||||
| api/users/key/client | key__client() | get | 客户端KEY |
|
||||
| api/users/bot/list | bot__list() | get | 机器人列表 |
|
||||
| api/users/bot/info | bot__info() | get | 机器人信息 |
|
||||
| api/users/bot/edit | bot__edit() | post | 添加、编辑机器人 |
|
||||
| api/users/bot/delete | bot__delete() | get | 删除机器人 |
|
||||
| api/users/share/list | share__list() | get | 获取分享列表 |
|
||||
| api/users/annual/report | annual__report() | get | 年度报告 |
|
||||
| api/users/device/list | device__list() | get | 获取设备列表 |
|
||||
| api/users/device/logout | device__logout() | get | 登出设备(删除设备) |
|
||||
| api/users/device/edit | device__edit() | get | 编辑设备 |
|
||||
| api/users/task/browse | task__browse() | get | 获取任务浏览历史 |
|
||||
| api/users/task/browse_save | task__browse_save() | get | 记录任务浏览历史 |
|
||||
| api/users/task/browse_clean | task__browse_clean() | post | 清理任务浏览历史 |
|
||||
| api/users/recent/browse | recent__browse() | get | 获取最近访问记录 |
|
||||
| api/users/recent/delete | recent__delete() | post | 删除最近访问记录 |
|
||||
| api/users/appsort | appsort() | get | 获取个人应用排序 |
|
||||
| api/users/appsort/save | appsort__save() | post | 保存个人应用排序 |
|
||||
| api/users/favorites | favorites() | get | 获取用户收藏列表 |
|
||||
| api/users/favorite/toggle | favorite__toggle() | post | 切换收藏状态 |
|
||||
| api/users/favorite/remark | favorite__remark() | post | 修改收藏备注 |
|
||||
| api/users/favorites/clean | favorites__clean() | post | 清理用户收藏 |
|
||||
| api/users/favorite/check | favorite__check() | get | 检查收藏状态 |
|
||||
|
||||
## project(ProjectController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/project/lists | lists() | get | 获取项目列表 |
|
||||
| api/project/one | one() | get | 获取一个项目信息 |
|
||||
| api/project/add | add() | get | 添加项目 |
|
||||
| api/project/update | update() | get | 修改项目 |
|
||||
| api/project/user | user() | post | 修改项目成员 |
|
||||
| api/project/invite | invite() | get | 获取邀请链接 |
|
||||
| api/project/invite/info | invite__info() | get | 通过邀请链接code获取项目信息 |
|
||||
| api/project/invite/join | invite__join() | get | 通过邀请链接code加入项目 |
|
||||
| api/project/transfer | transfer() | get | 移交项目 |
|
||||
| api/project/adddeputy | adddeputy() | post | 任命项目管理员(仅负责人可操作) |
|
||||
| api/project/deldeputy | deldeputy() | post | 罢免项目管理员(仅负责人可操作) |
|
||||
| api/project/sort | sort() | post | 排序任务 |
|
||||
| api/project/user/sort | user__sort() | post | 项目列表排序 |
|
||||
| api/project/exit | exit() | get | 退出项目 |
|
||||
| api/project/archived | archived() | get | 归档项目 |
|
||||
| api/project/remove | remove() | get | 删除项目 |
|
||||
| api/project/column/lists | column__lists() | get | 获取任务列表 |
|
||||
| api/project/column/add | column__add() | get | 添加任务列表 |
|
||||
| api/project/column/update | column__update() | get | 修改任务列表 |
|
||||
| api/project/column/remove | column__remove() | get | 删除任务列表 |
|
||||
| api/project/column/one | column__one() | get | 获取任务列详细 |
|
||||
| api/project/task/lists | task__lists() | get | 任务列表 |
|
||||
| api/project/user/projects | user__projects() | get | 会员参与的项目列表 |
|
||||
| api/project/user/tasks | user__tasks() | get | 会员参与的任务列表 |
|
||||
| api/project/user/counts | user__counts() | get | 会员参与的项目/任务数量 |
|
||||
| api/project/task/easylists | task__easylists() | get | 任务列表-简单的 |
|
||||
| api/project/task/export | task__export() | get | 导出任务(限管理员) |
|
||||
| api/project/task/exportoverdue | task__exportoverdue() | get | 导出超期任务(限管理员) |
|
||||
| api/project/task/down | task__down() | get | 下载导出的任务 |
|
||||
| api/project/task/one | task__one() | get | 获取单个任务信息 |
|
||||
| api/project/task/subdata | task__subdata() | get | 获取子任务数据 |
|
||||
| api/project/task/related | task__related() | get | 获取任务关联任务列表 |
|
||||
| api/project/task/related/delete | task__related__delete() | post | 删除任务关联 |
|
||||
| api/project/task/content | task__content() | get | 获取任务详细描述 |
|
||||
| api/project/task/content_history | task__content_history() | get | 获取任务详细历史描述 |
|
||||
| api/project/task/files | task__files() | get | 获取任务文件列表 |
|
||||
| api/project/task/filedelete | task__filedelete() | get | 删除任务文件 |
|
||||
| api/project/task/filedetail | task__filedetail() | get | 获取任务文件详情 |
|
||||
| api/project/task/filedown | task__filedown() | get | 下载任务文件 |
|
||||
| api/project/task/add | task__add() | post | 添加任务 |
|
||||
| api/project/task/addsub | task__addsub() | get | 添加子任务 |
|
||||
| api/project/task/upgrade | task__upgrade() | get | 子任务升级为主任务 |
|
||||
| api/project/task/update | task__update() | post | 修改任务、子任务 |
|
||||
| api/project/task/dialog | task__dialog() | get | 创建/获取聊天室 |
|
||||
| api/project/task/archived | task__archived() | get | 归档任务 |
|
||||
| api/project/task/remove | task__remove() | get | 删除任务 |
|
||||
| api/project/task/resetfromlog | task__resetfromlog() | get | 根据日志重置任务 |
|
||||
| api/project/task/flow | task__flow() | get | 任务工作流信息 |
|
||||
| api/project/task/move | task__move() | get | 任务移动 |
|
||||
| api/project/task/copy | task__copy() | post | 复制任务 |
|
||||
| api/project/task/ai_generate | task__ai_generate() | any | |
|
||||
| api/project/ai/generate | ai__generate() | any | |
|
||||
| api/project/flow/list | flow__list() | get | 工作流列表 |
|
||||
| api/project/flow/save | flow__save() | post | 保存工作流 |
|
||||
| api/project/flow/delete | flow__delete() | get | 删除工作流 |
|
||||
| api/project/log/lists | log__lists() | get | 获取项目、任务日志 |
|
||||
| api/project/top | top() | get | 项目置顶 |
|
||||
| api/project/permission | permission() | get | 获取项目权限设置 |
|
||||
| api/project/permission/update | permission__update() | get | 项目权限设置 |
|
||||
| api/project/task/template_list | task__template_list() | get | 任务模板列表 |
|
||||
| api/project/task/template_visible | task__template_visible() | get | 当前用户跨项目可见的全部任务模板 |
|
||||
| api/project/task/template_search | task__template_search() | get | 跨项目模板搜索分页 |
|
||||
| api/project/task/template_save | task__template_save() | post | 保存任务模板 |
|
||||
| api/project/task/template_sort | task__template_sort() | post | 排序任务模板 |
|
||||
| api/project/task/template_delete | task__template_delete() | get | 删除任务模板 |
|
||||
| api/project/task/template_default | task__template_default() | get | 设置(取消)任务模板为默认 |
|
||||
| api/project/tag/save | tag__save() | post | 保存标签 |
|
||||
| api/project/tag/sort | tag__sort() | post | 标签排序 |
|
||||
| api/project/tag/delete | tag__delete() | get | 删除标签 |
|
||||
| api/project/tag/list | tag__list() | get | 标签列表 |
|
||||
| api/project/task/ai_apply | task__ai_apply() | post | 采纳AI建议 |
|
||||
| api/project/task/ai_dismiss | task__ai_dismiss() | post | 忽略AI建议 |
|
||||
|
||||
## system(SystemController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/system/setting | setting() | get | 获取设置、保存设置 |
|
||||
| api/system/setting/email | setting__email() | get | 获取邮箱设置、保存邮箱设置(限管理员) |
|
||||
| api/system/setting/meeting | setting__meeting() | get | 获取会议设置、保存会议设置(限管理员) |
|
||||
| api/system/setting/ai | setting__ai() | any | |
|
||||
| api/system/setting/aibot | setting__aibot() | get | 获取AI设置、保存AI机器人设置(限管理员) |
|
||||
| api/system/setting/aibot_models | setting__aibot_models() | any | |
|
||||
| api/system/setting/aibot_defmodels | setting__aibot_defmodels() | any | |
|
||||
| api/system/setting/checkin | setting__checkin() | get | 获取签到设置、保存签到设置(限管理员) |
|
||||
| api/system/setting/apppush | setting__apppush() | get | 获取APP推送设置、保存APP推送设置(限管理员) |
|
||||
| api/system/setting/thirdaccess | setting__thirdaccess() | get | 第三方帐号(限管理员) |
|
||||
| api/system/setting/file | setting__file() | get | 文件设置(限管理员) |
|
||||
| api/system/demo | demo() | get | 获取演示帐号 |
|
||||
| api/system/priority | priority() | post | 任务优先级 |
|
||||
| api/system/microapp_menu | microapp_menu() | post | 自定义应用菜单 |
|
||||
| api/system/column/template | column__template() | post | 创建项目模板 |
|
||||
| api/system/license | license() | post | License |
|
||||
| api/system/get/info | get__info() | get | 获取终端详细信息 |
|
||||
| api/system/get/ip | get__ip() | get | 获取IP地址 |
|
||||
| api/system/get/cnip | get__cnip() | get | 是否中国IP地址 |
|
||||
| api/system/imgupload | imgupload() | post | 上传图片 |
|
||||
| api/system/imgview | imgview() | get | 浏览图片空间 |
|
||||
| api/system/fileupload | fileupload() | post | 上传文件 |
|
||||
| api/system/get/updatelog | get__updatelog() | get | 获取更新日志 |
|
||||
| api/system/email/check | email__check() | get | 邮件发送测试(限管理员) |
|
||||
| api/system/checkin/export | checkin__export() | get | 导出签到数据(限管理员) |
|
||||
| api/system/checkin/down | checkin__down() | get | 下载导出的签到数据 |
|
||||
| api/system/version | version() | get | 获取版本号 |
|
||||
| api/system/prefetch | prefetch() | get | 预加载的资源 |
|
||||
|
||||
## dialog(DialogController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/dialog/lists | lists() | get | 对话列表 |
|
||||
| api/dialog/beyond | beyond() | get | 列表外对话 |
|
||||
| api/dialog/search | search() | get | 搜索会话 |
|
||||
| api/dialog/search/tag | search__tag() | get | 搜索标注会话 |
|
||||
| api/dialog/one | one() | get | 获取单个会话信息 |
|
||||
| api/dialog/user | user() | get | 获取会话成员 |
|
||||
| api/dialog/todo | todo() | get | 获取会话待办 |
|
||||
| api/dialog/top | top() | get | 会话置顶 |
|
||||
| api/dialog/hide | hide() | get | 会话隐藏 |
|
||||
| api/dialog/tel | tel() | get | 获取对方联系电话 |
|
||||
| api/dialog/open/user | open__user() | get | 打开会话 |
|
||||
| api/dialog/open/event | open__event() | get | 打开会话事件 |
|
||||
| api/dialog/msg/list | msg__list() | get | 获取消息列表 |
|
||||
| api/dialog/msg/latest | msg__latest() | get | 获取最新消息列表 |
|
||||
| api/dialog/msg/one | msg__one() | get | 获取单条消息 |
|
||||
| api/dialog/msg/dot | msg__dot() | get | 聊天消息去除点 |
|
||||
| api/dialog/msg/read | msg__read() | get | 已读聊天消息 |
|
||||
| api/dialog/msg/unread | msg__unread() | get | 获取未读消息数据 |
|
||||
| api/dialog/msg/checked | msg__checked() | get | 设置消息checked |
|
||||
| api/dialog/msg/stream | msg__stream() | post | 通知成员监听消息 |
|
||||
| api/dialog/msg/ai_generate | msg__ai_generate() | any | |
|
||||
| api/dialog/msg/sendtext | msg__sendtext() | post | 发送消息 |
|
||||
| api/dialog/msg/sendnotice | msg__sendnotice() | post | 发送通知 |
|
||||
| api/dialog/msg/sendtemplate | msg__sendtemplate() | post | 发送模板消息 |
|
||||
| api/dialog/msg/sendrecord | msg__sendrecord() | post | 发送语音 |
|
||||
| api/dialog/msg/convertrecord | msg__convertrecord() | post | 录音转文字 |
|
||||
| api/dialog/msg/sendfile | msg__sendfile() | post | 文件上传 |
|
||||
| api/dialog/msg/sendfiles | msg__sendfiles() | post | 群发文件上传 |
|
||||
| api/dialog/msg/sendfileid | msg__sendfileid() | get | 通过文件ID发送文件 |
|
||||
| api/dialog/msg/sendtaskid | msg__sendtaskid() | get | 通过任务ID发送任务 |
|
||||
| api/dialog/msg/sendanon | msg__sendanon() | post | 发送匿名消息 |
|
||||
| api/dialog/msg/sendbot | msg__sendbot() | post | 发送机器人消息 |
|
||||
| api/dialog/msg/send_ai_assistant | msg__send_ai_assistant() | post | 以AI助手身份发送消息到对话 |
|
||||
| api/dialog/msg/sendlocation | msg__sendlocation() | post | 发送位置消息 |
|
||||
| api/dialog/msg/readlist | msg__readlist() | get | 获取消息阅读情况 |
|
||||
| api/dialog/msg/detail | msg__detail() | get | 消息详情 |
|
||||
| api/dialog/msg/download | msg__download() | get | 文件下载 |
|
||||
| api/dialog/msg/withdraw | msg__withdraw() | get | 聊天消息撤回 |
|
||||
| api/dialog/msg/voice2text | msg__voice2text() | get | 语音消息转文字 |
|
||||
| api/dialog/msg/translation | msg__translation() | get | 翻译消息 |
|
||||
| api/dialog/msg/mark | msg__mark() | get | 消息标记操作 |
|
||||
| api/dialog/msg/silence | msg__silence() | get | 消息免打扰 |
|
||||
| api/dialog/msg/forward | msg__forward() | get | 转发消息给 |
|
||||
| api/dialog/msg/mergeforward | msg__mergeforward() | get | 合并转发消息 |
|
||||
| api/dialog/msg/mergedetail | msg__mergedetail() | get | 合并转发消息详情 |
|
||||
| api/dialog/msg/emoji | msg__emoji() | get | emoji回复 |
|
||||
| api/dialog/msg/tag | msg__tag() | get | 标注/取消标注 |
|
||||
| api/dialog/msg/todo | msg__todo() | get | 设待办/取消待办 |
|
||||
| api/dialog/msg/todolist | msg__todolist() | get | 获取消息待办情况 |
|
||||
| api/dialog/msg/todoremind | msg__todoremind() | post | 设置/修改/取消待办提醒时间 |
|
||||
| api/dialog/msg/done | msg__done() | get | 完成待办 |
|
||||
| api/dialog/msg/color | msg__color() | get | 设置颜色 |
|
||||
| api/dialog/msg/webhookmsg2ai | msg__webhookmsg2ai() | any | |
|
||||
| api/dialog/group/add | group__add() | get | 新增群组 |
|
||||
| api/dialog/group/edit | group__edit() | get | 修改群组 |
|
||||
| api/dialog/group/adduser | group__adduser() | get | 添加群成员 |
|
||||
| api/dialog/group/deluser | group__deluser() | get | 移出(退出)群成员 |
|
||||
| api/dialog/group/transfer | group__transfer() | get | 转让群组 |
|
||||
| api/dialog/group/adddeputy | group__adddeputy() | any | |
|
||||
| api/dialog/group/deldeputy | group__deldeputy() | any | |
|
||||
| api/dialog/group/disband | group__disband() | get | 解散群组 |
|
||||
| api/dialog/group/searchuser | group__searchuser() | get | 搜索个人群(仅限管理员) |
|
||||
| api/dialog/common/list | common__list() | get | 共同群组群聊 |
|
||||
| api/dialog/okr/add | okr__add() | post | 创建OKR评论会话 |
|
||||
| api/dialog/okr/push | okr__push() | post | 推送OKR相关信息 |
|
||||
| api/dialog/msg/wordchain | msg__wordchain() | post | 发送接龙消息 |
|
||||
| api/dialog/msg/vote | msg__vote() | post | 发起投票 |
|
||||
| api/dialog/msg/top | msg__top() | get | 置顶/取消置顶 |
|
||||
| api/dialog/msg/topinfo | msg__topinfo() | get | 获取置顶消息 |
|
||||
| api/dialog/msg/applied | msg__applied() | any | |
|
||||
| api/dialog/sticker/search | sticker__search() | get | 搜索在线表情 |
|
||||
| api/dialog/config | config() | get | 获取会话配置 |
|
||||
| api/dialog/config/save | config__save() | post | 保存会话配置 |
|
||||
| api/dialog/session/create | session__create() | get | AI-开启新会话 |
|
||||
| api/dialog/session/list | session__list() | get | AI-获取会话列表 |
|
||||
| api/dialog/session/open | session__open() | get | AI-打开会话 |
|
||||
| api/dialog/session/rename | session__rename() | post | AI-重命名会话 |
|
||||
|
||||
## file(FileController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/file/lists | lists() | get | 获取文件列表 |
|
||||
| api/file/one | one() | get | 获取单条数据 |
|
||||
| api/file/fetch | fetch() | get | 通过路径获取文件文本内容 |
|
||||
| api/file/search | search() | get | 搜索文件列表 |
|
||||
| api/file/add | add() | get | 添加、修改文件(夹) |
|
||||
| api/file/copy | copy() | get | 复制文件(夹) |
|
||||
| api/file/move | move() | get | 移动文件(夹) |
|
||||
| api/file/remove | remove() | get | 删除文件(夹) |
|
||||
| api/file/content | content() | get | 获取文件内容 |
|
||||
| api/file/content/save | content__save() | get | 保存文件内容 |
|
||||
| api/file/office/token | office__token() | get | 获取token |
|
||||
| api/file/content/office | content__office() | get | 保存文件内容(office) |
|
||||
| api/file/content/upload | content__upload() | get | 保存文件内容(上传文件) |
|
||||
| api/file/content/history | content__history() | get | 获取内容历史 |
|
||||
| api/file/content/restore | content__restore() | get | 恢复文件历史 |
|
||||
| api/file/share | share() | get | 获取共享信息 |
|
||||
| api/file/share/update | share__update() | get | 设置共享 |
|
||||
| api/file/share/out | share__out() | get | 退出共享 |
|
||||
| api/file/link | link() | get | 获取链接 |
|
||||
| api/file/download/pack | download__pack() | get | 打包文件 |
|
||||
|
||||
## report(ReportController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/report/my | my() | get | 我发送的汇报 |
|
||||
| api/report/receive | receive() | get | 我接收的汇报 |
|
||||
| api/report/store | store() | get | 保存并发送工作汇报 |
|
||||
| api/report/template | template() | get | 生成汇报模板 |
|
||||
| api/report/detail | detail() | get | 报告详情 |
|
||||
| api/report/analysave | analysave() | post | 保存工作汇报 AI 分析 |
|
||||
| api/report/mark | mark() | get | 标记已读/未读 |
|
||||
| api/report/share | share() | get | 分享报告到消息 |
|
||||
| api/report/last_submitter | last_submitter() | get | 获取最后一次提交的接收人 |
|
||||
| api/report/unread | unread() | get | 获取未读 |
|
||||
| api/report/read | read() | get | 标记汇报已读,可批量 |
|
||||
|
||||
## public(PublicController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/public/checkin/install | checkin__install() | any | |
|
||||
| api/public/checkin/report | checkin__report() | any | |
|
||||
|
||||
## approve(ApproveController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/approve/verifyToken | verifyToken() | get | 验证APi登录 |
|
||||
| api/approve/procdef/all | procdef__all() | post | 查询流程定义 |
|
||||
| api/approve/procdef/del | procdef__del() | get | 删除流程定义 |
|
||||
| api/approve/process/start | process__start() | post | 启动流程(审批中) |
|
||||
| api/approve/process/addGlobalComment | process__addGlobalComment() | post | 添加全局评论 |
|
||||
| api/approve/task/complete | task__complete() | post | 审批 |
|
||||
| api/approve/task/withdraw | task__withdraw() | post | 撤回 |
|
||||
| api/approve/process/delById | process__delById() | post | 删除审批(流程实例) |
|
||||
| api/approve/process/findTask | process__findTask() | post | 查询需要我审批的流程(审批中) |
|
||||
| api/approve/process/startByMyselfAll | process__startByMyselfAll() | post | 查询我启动的流程(全部) |
|
||||
| api/approve/process/startByMyself | process__startByMyself() | post | 查询我启动的流程(审批中) |
|
||||
| api/approve/process/findProcNotify | process__findProcNotify() | post | 查询抄送我的流程(审批中) |
|
||||
| api/approve/identitylink/findParticipant | identitylink__findParticipant() | get | 查询流程实例的参与者(审批中) |
|
||||
| api/approve/procHistory/findTask | procHistory__findTask() | post | 查询需要我审批的流程(已结束) |
|
||||
| api/approve/procHistory/startByMyself | procHistory__startByMyself() | post | 查询我启动的流程(已结束) |
|
||||
| api/approve/procHistory/findProcNotify | procHistory__findProcNotify() | post | 查询抄送我的流程(已结束) |
|
||||
| api/approve/identitylinkHistory/findParticipant | identitylinkHistory__findParticipant() | get | 查询流程实例的参与者(已结束) |
|
||||
| api/approve/process/detail | process__detail() | get | 根据流程ID查询流程详情 |
|
||||
| api/approve/export | export() | post | 导出数据 |
|
||||
| api/approve/getStateDescription | getStateDescription() | any | |
|
||||
| api/approve/down | down() | get | 下载导出的审批数据 |
|
||||
| api/approve/handleParticipant | handleParticipant() | any | |
|
||||
| api/approve/approveMsg | approveMsg() | any | |
|
||||
| api/approve/getProcessById | getProcessById() | any | |
|
||||
| api/approve/handleProcessNode | handleProcessNode() | any | |
|
||||
| api/approve/getUserProcessParticipantById | getUserProcessParticipantById() | any | |
|
||||
| api/approve/user/status | user__status() | get | 获取用户审批状态 |
|
||||
| api/approve/process/doto | process__doto() | get | 查询需要我审批的流程数量 |
|
||||
|
||||
## assistant(AssistantController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/assistant/auth | auth() | post | 生成授权码 |
|
||||
| api/assistant/models | models() | get | 获取AI模型 |
|
||||
| api/assistant/match_elements | match_elements() | post | 元素向量匹配 |
|
||||
| api/assistant/log/search | log__search() | post | 记录帮助知识库检索日志 |
|
||||
| api/assistant/feedback/save | feedback__save() | post | 保存回复反馈 |
|
||||
| api/assistant/operation/dispatch | operation__dispatch() | post | 派发页面操作 |
|
||||
| api/assistant/operation/result | operation__result() | get | 取页面操作结果 |
|
||||
| api/assistant/session/list | session__list() | any | |
|
||||
| api/assistant/session/save | session__save() | any | |
|
||||
| api/assistant/session/delete | session__delete() | any | |
|
||||
|
||||
## complaint(ComplaintController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/complaint/lists | lists() | get | 获取举报投诉列表 |
|
||||
| api/complaint/submit | submit() | post | 举报投诉 |
|
||||
| api/complaint/action | action() | post | 举报投诉 - 操作 |
|
||||
|
||||
## search(SearchController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| api/search/contact | contact() | get | 搜索联系人 |
|
||||
| api/search/project | project() | get | 搜索项目 |
|
||||
| api/search/task | task() | get | 搜索任务 |
|
||||
| api/search/file | file() | get | 搜索文件 |
|
||||
| api/search/message | message() | get | 搜索消息 |
|
||||
|
||||
## test(TestController)
|
||||
|
||||
| URL | 方法名 | HTTP | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
173
scripts/check-language.mjs
Normal file
173
scripts/check-language.mjs
Normal file
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 前端翻译文案校验脚本
|
||||
*
|
||||
* 递归扫描 resources/assets/js 下的 .js/.vue 文件,提取
|
||||
* $L('...') / $L("...") / this.$L(...) / $A.L(...) 的第一个字符串字面量参数,
|
||||
* 与 language/original-web.txt 的行集合(trim 后)比对:
|
||||
* - 代码中有、txt 中没有 → 「缺失文案」,列出文案及所有出现位置,exit 1
|
||||
* - txt 中有、代码中没有 → 「疑似未使用」,仅输出数量与前 20 条样例,不影响退出码
|
||||
*
|
||||
* 用法:node scripts/check-language.mjs
|
||||
* 零第三方依赖,要求 Node >= 20。
|
||||
*/
|
||||
|
||||
import { readFileSync, readdirSync, statSync } from "node:fs";
|
||||
import { join, relative, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const SCAN_DIR = join(ROOT, "resources", "assets", "js");
|
||||
const TXT_FILE = join(ROOT, "language", "original-web.txt");
|
||||
|
||||
// ---------- 工具函数 ----------
|
||||
|
||||
/** 递归收集 .js/.vue 文件 */
|
||||
function collectFiles(dir, out = []) {
|
||||
for (const name of readdirSync(dir)) {
|
||||
const full = join(dir, name);
|
||||
const st = statSync(full);
|
||||
if (st.isDirectory()) {
|
||||
collectFiles(full, out);
|
||||
} else if (st.isFile() && /\.(js|vue)$/.test(name)) {
|
||||
out.push(full);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 反转义字符串字面量中的常见转义序列 */
|
||||
function unescapeLiteral(raw) {
|
||||
return raw.replace(/\\(.)/g, (_, ch) => {
|
||||
switch (ch) {
|
||||
case "n": return "\n";
|
||||
case "t": return "\t";
|
||||
case "r": return "\r";
|
||||
default: return ch; // \' \" \\ 以及其他都还原为字符本身
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 content 的 pos(指向开引号 ' 或 ")开始解析字符串字面量。
|
||||
* 返回 { raw, end }:raw 为引号内原始文本(未反转义),end 指向闭引号的下一位;
|
||||
* 未闭合返回 null。
|
||||
*/
|
||||
function parseStringLiteral(content, pos) {
|
||||
const quote = content[pos];
|
||||
let i = pos + 1;
|
||||
let raw = "";
|
||||
while (i < content.length) {
|
||||
const ch = content[i];
|
||||
if (ch === "\\") {
|
||||
if (i + 1 >= content.length) return null;
|
||||
raw += ch + content[i + 1];
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if (ch === quote) {
|
||||
return { raw, end: i + 1 };
|
||||
}
|
||||
if (ch === "\n") return null; // 普通字符串字面量不允许裸换行
|
||||
raw += ch;
|
||||
i++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取单个文件中所有翻译调用的第一个字符串字面量参数。
|
||||
* 返回 [{ text, line }]。
|
||||
* 第一个参数不是普通字符串字面量(模板字符串、变量、函数调用等),
|
||||
* 或字面量后紧跟 + (拼接表达式)时跳过,不报错。
|
||||
*/
|
||||
function extractCalls(content) {
|
||||
const results = [];
|
||||
// $L( / this.$L( —— 都含 "$L(",要求 $ 前不是标识符字符或 $;$A.L( 单独匹配
|
||||
const callRe = /(?<![\w$])\$L\s*\(|\$A\.L\s*\(/g;
|
||||
let m;
|
||||
while ((m = callRe.exec(content)) !== null) {
|
||||
let i = m.index + m[0].length;
|
||||
// 跳过空白(含换行)
|
||||
while (i < content.length && /\s/.test(content[i])) i++;
|
||||
const ch = content[i];
|
||||
if (ch !== "'" && ch !== '"') continue; // 模板字符串、变量、其他表达式:跳过
|
||||
const lit = parseStringLiteral(content, i);
|
||||
if (!lit) continue;
|
||||
// 看字面量后第一个非空白字符:是 + 则为拼接表达式,跳过
|
||||
let j = lit.end;
|
||||
while (j < content.length && /\s/.test(content[j])) j++;
|
||||
if (content[j] === "+") continue;
|
||||
const text = unescapeLiteral(lit.raw).trim();
|
||||
if (!text) continue;
|
||||
const line = content.slice(0, m.index).split("\n").length;
|
||||
results.push({ text, line });
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// ---------- 主流程 ----------
|
||||
|
||||
// 1. 读取 original-web.txt 行集合(trim 后,忽略空行)
|
||||
const txtLines = new Set(
|
||||
readFileSync(TXT_FILE, "utf8")
|
||||
.split("\n")
|
||||
.map(l => l.trim())
|
||||
.filter(Boolean)
|
||||
);
|
||||
|
||||
// 2. 扫描源码并提取
|
||||
const files = collectFiles(SCAN_DIR);
|
||||
/** Map<text, Array<"相对路径:行号">> */
|
||||
const usages = new Map();
|
||||
for (const file of files) {
|
||||
const rel = relative(ROOT, file);
|
||||
const content = readFileSync(file, "utf8");
|
||||
for (const { text, line } of extractCalls(content)) {
|
||||
if (!usages.has(text)) usages.set(text, []);
|
||||
usages.get(text).push(`${rel}:${line}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 比对
|
||||
const missing = []; // 代码有、txt 无
|
||||
for (const [text, locations] of usages) {
|
||||
if (!txtLines.has(text)) {
|
||||
missing.push({ text, locations });
|
||||
}
|
||||
}
|
||||
const unused = [...txtLines].filter(l => !usages.has(l)); // txt 有、代码无
|
||||
|
||||
// 4. 输出
|
||||
if (missing.length > 0) {
|
||||
console.log("== 缺失文案(代码中使用但 language/original-web.txt 中没有)==\n");
|
||||
for (const { text, locations } of missing) {
|
||||
console.log(` 「${text}」`);
|
||||
for (const loc of locations) {
|
||||
console.log(` ${loc}`);
|
||||
}
|
||||
}
|
||||
console.log("");
|
||||
}
|
||||
|
||||
console.log("== 汇总 ==");
|
||||
console.log(` 扫描文件数: ${files.length}`);
|
||||
console.log(` 提取字面量数(去重): ${usages.size}`);
|
||||
console.log(` 缺失数: ${missing.length}`);
|
||||
console.log(` 疑似未使用数: ${unused.length}`);
|
||||
|
||||
if (unused.length > 0) {
|
||||
console.log("\n== 疑似未使用(txt 中有、代码中未发现,仅提示,前 20 条样例)==");
|
||||
for (const text of unused.slice(0, 20)) {
|
||||
console.log(` ${text}`);
|
||||
}
|
||||
if (unused.length > 20) {
|
||||
console.log(` ...(共 ${unused.length} 条)`);
|
||||
}
|
||||
}
|
||||
|
||||
if (missing.length > 0) {
|
||||
console.log(`\n校验失败:存在 ${missing.length} 条缺失文案,请将原文追加到 language/original-web.txt`);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log("\n校验通过:未发现缺失文案。");
|
||||
155
scripts/gen-events-map.mjs
Normal file
155
scripts/gen-events-map.mjs
Normal file
@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 事件总线注册表生成脚本
|
||||
*
|
||||
* 扫描前端代码中 mitt 事件总线(resources/assets/js/store/events.js 导出的 emitter)
|
||||
* 的 emit/on/off 调用,按事件名聚合生成 docs/events-map.md。
|
||||
*
|
||||
* 用法: node scripts/gen-events-map.mjs
|
||||
* 零第三方依赖(仅 node:fs / node:path)。
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
// ===== 常量配置 =====
|
||||
const ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
// 扫描范围(相对仓库根目录)
|
||||
const SCAN_DIR = 'resources/assets/js';
|
||||
// 输出文件(相对仓库根目录)
|
||||
const OUTPUT_FILE = 'docs/events-map.md';
|
||||
// 扫描的文件扩展名
|
||||
const EXTENSIONS = new Set(['.js', '.vue']);
|
||||
// ====================
|
||||
|
||||
// 匹配 emitter.emit( / emitter.on( / emitter.off(
|
||||
// 负向断言排除 xxx.emitter.emit(如 Quill 的 this.quill.emitter,不是 mitt 总线)
|
||||
const CALL_RE = /(?<![.\w])emitter\.(emit|on|off)\s*\(/g;
|
||||
// 第一参数为字符串字面量:'xxx' 或 "xxx"
|
||||
const LITERAL_RE = /^\s*(['"])((?:\\.|(?!\1).)*?)\1/;
|
||||
|
||||
/** 递归收集待扫描文件 */
|
||||
function collectFiles(dir) {
|
||||
const result = [];
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
result.push(...collectFiles(full));
|
||||
} else if (entry.isFile() && EXTENSIONS.has(path.extname(entry.name))) {
|
||||
result.push(full);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 由字符偏移计算行号(1-based) */
|
||||
function lineOf(content, offset) {
|
||||
let line = 1;
|
||||
for (let i = 0; i < offset; i++) {
|
||||
if (content.charCodeAt(i) === 10) line++;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
const scanRoot = path.join(ROOT_DIR, SCAN_DIR);
|
||||
const files = collectFiles(scanRoot).sort();
|
||||
|
||||
// events: Map<eventName, {emit: loc[], on: loc[], off: loc[]}>
|
||||
const events = new Map();
|
||||
// 动态事件名(第一参数非字符串字面量)
|
||||
const dynamics = [];
|
||||
let totalCalls = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const content = fs.readFileSync(file, 'utf8');
|
||||
const rel = path.relative(ROOT_DIR, file).split(path.sep).join('/');
|
||||
let m;
|
||||
CALL_RE.lastIndex = 0;
|
||||
while ((m = CALL_RE.exec(content)) !== null) {
|
||||
totalCalls++;
|
||||
const method = m[1];
|
||||
const argStart = m.index + m[0].length;
|
||||
const line = lineOf(content, m.index);
|
||||
const loc = `${rel}:${line}`;
|
||||
const rest = content.slice(argStart, argStart + 200);
|
||||
const lit = rest.match(LITERAL_RE);
|
||||
if (lit) {
|
||||
const name = lit[2];
|
||||
if (!events.has(name)) {
|
||||
events.set(name, { emit: [], on: [], off: [] });
|
||||
}
|
||||
events.get(name)[method].push(loc);
|
||||
} else {
|
||||
// 截取第一参数片段用于展示
|
||||
const snippet = rest.split(/[\n,)]/)[0].trim();
|
||||
dynamics.push({ method, loc, snippet });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const names = [...events.keys()].sort();
|
||||
|
||||
// 统计
|
||||
const deadEmit = names.filter(n => events.get(n).emit.length > 0 && events.get(n).on.length === 0);
|
||||
const deadOn = names.filter(n => events.get(n).on.length > 0 && events.get(n).emit.length === 0);
|
||||
|
||||
// ===== 生成 Markdown =====
|
||||
const out = [];
|
||||
out.push('# 前端事件总线注册表');
|
||||
out.push('');
|
||||
out.push('> **本文件由脚本自动生成,请勿手改。**');
|
||||
out.push('>');
|
||||
out.push('> - 生成命令: `node scripts/gen-events-map.mjs`');
|
||||
out.push(`> - 扫描范围: \`${SCAN_DIR}\` 下所有 \`.js\` / \`.vue\` 文件(共 ${files.length} 个)`);
|
||||
out.push('> - 事件总线: `resources/assets/js/store/events.js`(mitt 实例)');
|
||||
out.push('> - 仅匹配裸 `emitter.emit/on/off(` 调用;`xxx.emitter.emit(`(如 Quill 内部 emitter)不属于本总线,已排除');
|
||||
out.push('');
|
||||
out.push(`共 **${names.length}** 个静态可解析事件,**${totalCalls}** 处 \`emitter.emit/on/off\` 调用。`);
|
||||
out.push('');
|
||||
out.push('## 事件清单');
|
||||
out.push('');
|
||||
|
||||
for (const name of names) {
|
||||
const ev = events.get(name);
|
||||
out.push(`### \`${name}\``);
|
||||
out.push('');
|
||||
out.push(`- **emit(${ev.emit.length})**${ev.emit.length ? '' : ':无(疑似死事件)'}`);
|
||||
for (const loc of ev.emit) out.push(` - \`${loc}\``);
|
||||
out.push(`- **on(${ev.on.length})**${ev.on.length ? '' : ':无(无人监听)'}`);
|
||||
for (const loc of ev.on) out.push(` - \`${loc}\``);
|
||||
if (ev.off.length) {
|
||||
out.push(`- **off(${ev.off.length})**`);
|
||||
for (const loc of ev.off) out.push(` - \`${loc}\``);
|
||||
}
|
||||
out.push('');
|
||||
}
|
||||
|
||||
out.push('## 动态事件名(无法静态解析)');
|
||||
out.push('');
|
||||
if (dynamics.length === 0) {
|
||||
out.push('无。');
|
||||
} else {
|
||||
out.push('以下调用的第一参数不是字符串字面量,无法静态解析事件名:');
|
||||
out.push('');
|
||||
for (const d of dynamics) {
|
||||
out.push(`- \`${d.loc}\` — \`emitter.${d.method}(${d.snippet}...)\``);
|
||||
}
|
||||
}
|
||||
out.push('');
|
||||
out.push('## 统计');
|
||||
out.push('');
|
||||
out.push(`- 事件总数(静态可解析): **${names.length}**`);
|
||||
out.push(`- 只 emit 无 on(疑似死事件): **${deadEmit.length}**${deadEmit.length ? ` — ${deadEmit.map(n => `\`${n}\``).join('、')}` : ''}`);
|
||||
out.push(`- 只 on 无 emit(无人发射): **${deadOn.length}**${deadOn.length ? ` — ${deadOn.map(n => `\`${n}\``).join('、')}` : ''}`);
|
||||
out.push(`- 动态事件名调用: **${dynamics.length}**`);
|
||||
out.push('');
|
||||
|
||||
const outputPath = path.join(ROOT_DIR, OUTPUT_FILE);
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
fs.writeFileSync(outputPath, out.join('\n'), 'utf8');
|
||||
|
||||
console.log(`[gen-events-map] 扫描 ${files.length} 个文件,${totalCalls} 处调用,${names.length} 个事件,${dynamics.length} 处动态事件名`);
|
||||
console.log(`[gen-events-map] 已生成 ${OUTPUT_FILE}`);
|
||||
console.log(`[gen-events-map] 只 emit 无 on: ${deadEmit.length ? deadEmit.join(', ') : '无'}`);
|
||||
console.log(`[gen-events-map] 只 on 无 emit: ${deadOn.length ? deadOn.join(', ') : '无'}`);
|
||||
666
types/dootask-globals.d.ts
vendored
Normal file
666
types/dootask-globals.d.ts
vendored
Normal file
@ -0,0 +1,666 @@
|
||||
/**
|
||||
* DooTask 前端全局工具 $A / 翻译函数 $L 的 TypeScript 类型声明(纯声明,无运行时代码)。
|
||||
*
|
||||
* 来源文件($A 是 jQuery 实例,由以下三个 IIFE 文件通过 $.extend() 扩展而成,
|
||||
* 挂载于 window.$A 与 Vue.prototype.$A):
|
||||
* - resources/assets/js/functions/common.js (基础函数 / localForage / Storage / ihttp / ajaxc / time / sort)
|
||||
* - resources/assets/js/functions/web.js (页面专用 / iviewui 弹窗提示 / dark 暗黑模式)
|
||||
* - resources/assets/js/functions/eeui.js (EEUI App 专用)
|
||||
* $L 来源:resources/assets/js/language/index.js 的 switchLanguage,
|
||||
* 挂载于 window.$L、Vue.prototype.$L 以及 $A.L(见 resources/assets/js/app.js)。
|
||||
*
|
||||
* 维护提示:在上述源文件中新增/修改 $.extend 挂载的 $A 方法时,须同步更新本声明文件。
|
||||
*/
|
||||
|
||||
/** modal 系列配置(字符串等价于 { content: 字符串 }) */
|
||||
interface DooTaskModalConfig {
|
||||
/** 标题,默认「温馨提示」 */
|
||||
title?: string;
|
||||
/** 内容 */
|
||||
content?: string | false;
|
||||
/** 确定按钮文字,默认「确定」 */
|
||||
okText?: string;
|
||||
/** 取消按钮文字,默认「取消」 */
|
||||
cancelText?: string;
|
||||
/** 传 false 时由调用方自行处理翻译,否则内部自动 $L 翻译 */
|
||||
language?: boolean;
|
||||
/** onOk 返回 Promise 时启用 loading 等待 */
|
||||
loading?: boolean;
|
||||
onOk?: (...args: any[]) => any;
|
||||
onCancel?: (...args: any[]) => any;
|
||||
render?: (...args: any[]) => any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/** notice 系列配置(字符串等价于 { desc: 字符串 }) */
|
||||
interface DooTaskNoticeConfig {
|
||||
/** 标题,默认「温馨提示」 */
|
||||
title?: string;
|
||||
/** 描述内容 */
|
||||
desc?: string;
|
||||
/** 显示时长(秒) */
|
||||
duration?: number;
|
||||
render?: (...args: any[]) => any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/** ajaxc 请求参数 */
|
||||
interface DooTaskAjaxcParams {
|
||||
url: string;
|
||||
data?: any;
|
||||
cache?: boolean;
|
||||
method?: string;
|
||||
timeout?: number;
|
||||
dataType?: string;
|
||||
header?: Record<string, string>;
|
||||
requestId?: string | number | null;
|
||||
before?: () => void;
|
||||
complete?: () => void;
|
||||
after?: (success: boolean) => void;
|
||||
success?: (data: any, status: number | string, xhr: XMLHttpRequest) => void;
|
||||
error?: (xhr: XMLHttpRequest, status: number | string) => void;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/** extractImageParameter 返回的图片参数 */
|
||||
interface DooTaskImageParameter {
|
||||
src: string | null;
|
||||
width: number;
|
||||
height: number;
|
||||
original: string;
|
||||
}
|
||||
|
||||
/** imageRatioHandle 参数/返回值 */
|
||||
interface DooTaskImageRatioParams {
|
||||
/** 原图地址 */
|
||||
src: string;
|
||||
/** 原图宽度 */
|
||||
width: number;
|
||||
/** 原图高度 */
|
||||
height: number;
|
||||
/** 裁剪参数,如:{ratio:3, percentage:"80x0"} */
|
||||
crops?: { ratio?: number; size?: string; percentage?: string; cover?: string; contain?: string };
|
||||
/** 返回尺寸缩放最高尺寸 */
|
||||
scaleSize?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/** dark 暗黑模式工具 */
|
||||
interface DooTaskDark {
|
||||
utils: {
|
||||
/** 判断浏览器支持的暗黑实现方式 chrome|webkit|null */
|
||||
supportMode(): "chrome" | "webkit" | null;
|
||||
/** 默认反色滤镜样式 */
|
||||
defaultFilter(): string;
|
||||
/** 反向反色滤镜样式 */
|
||||
reverseFilter(): string;
|
||||
/** 取消滤镜样式 */
|
||||
noneFilter(): string;
|
||||
/** 附加额外样式 */
|
||||
addExtraStyle(): string;
|
||||
/** 添加样式标签 */
|
||||
addStyle(id: string, tag: string, css: string): void;
|
||||
/** 获取元素 classList */
|
||||
getClassList(node: Element): DOMTokenList | any[];
|
||||
/** 给元素添加 class */
|
||||
addClass(node: Element, name: string): DooTaskDark["utils"];
|
||||
/** 移除元素 class */
|
||||
removeClass(node: Element, name: string): DooTaskDark["utils"];
|
||||
/** 判断元素是否含有 class */
|
||||
hasClass(node: Element, name: string): boolean;
|
||||
/** 根据 id 获取元素 */
|
||||
hasElementById(eleId: string): HTMLElement | null;
|
||||
/** 根据 id 删除元素 */
|
||||
removeElementById(eleId: string): void;
|
||||
};
|
||||
/** 创建暗黑模式样式 */
|
||||
createDarkStyle(): void;
|
||||
/** 开启暗黑模式 */
|
||||
enableDarkMode(): void;
|
||||
/** 关闭暗黑模式 */
|
||||
disableDarkMode(): void;
|
||||
/** 跟随系统自动切换暗黑模式 */
|
||||
autoDarkMode(): void;
|
||||
/** 是否已开启暗黑模式 */
|
||||
isDarkEnabled(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* DooTask 全局工具对象。
|
||||
* 说明:$A 本体是 jQuery 实例(window.$ / window.jQuery),为避免引入 @types/jquery 依赖,
|
||||
* 这里不 extends JQueryStatic,而是用「可调用签名 + 字符串索引签名」兜底 jQuery 本体的
|
||||
* 选择器调用(如 $A(el))与 each/extend 等静态方法。
|
||||
*/
|
||||
interface DooTaskGlobal {
|
||||
/** jQuery 选择器调用兜底($A(selector),返回 jQuery 对象) */
|
||||
(selector?: any, context?: any): any;
|
||||
/** jQuery 本体其余静态属性/方法兜底(each/extend/fn 等) */
|
||||
[key: string]: any;
|
||||
|
||||
/* =========================================================================
|
||||
* app.js 挂载的全局属性
|
||||
* ========================================================================= */
|
||||
|
||||
/** 翻译函数(同 window.$L,见 language/index.js switchLanguage) */
|
||||
L(text: string, ...args: Array<string | number>): string;
|
||||
/** 应用是否初始化完成 */
|
||||
Ready: boolean;
|
||||
/** Electron 桥接对象(非 Electron 环境为 null) */
|
||||
Electron: any;
|
||||
/** 运行平台:web|mac|win|ios|android */
|
||||
Platform: string;
|
||||
/** 是否 Electron 主窗口 */
|
||||
isMainElectron: boolean;
|
||||
/** 是否 Electron 子窗口 */
|
||||
isSubElectron: boolean;
|
||||
/** 是否 EEUI App 环境 */
|
||||
isEEUIApp: boolean;
|
||||
/** 是否 Electron 环境 */
|
||||
isElectron: boolean;
|
||||
/** 是否客户端软件环境(Electron 或 EEUI) */
|
||||
isSoftware: boolean;
|
||||
/** 是否开启调试日志(VConsole) */
|
||||
openLog: boolean;
|
||||
/** iView Modal 实例(app 初始化后可用) */
|
||||
Modal: any;
|
||||
/** iView Message 实例(app 初始化后可用) */
|
||||
Message: any;
|
||||
/** iView Notice 实例(app 初始化后可用) */
|
||||
Notice: any;
|
||||
|
||||
/* =========================================================================
|
||||
* common.js —— 基础函数类
|
||||
* ========================================================================= */
|
||||
|
||||
/** 是否数组 */
|
||||
isArray(obj: any): boolean;
|
||||
/** 规范化为整型数组(去重、过滤非正整数) */
|
||||
normalizeIntArray(data: any): number[];
|
||||
/** 是否数组对象(普通 JSON 对象) */
|
||||
isJson(obj: any): boolean;
|
||||
/** 是否在数组里(regular 为 true 时支持 * 通配) */
|
||||
inArray(key: any, array: any[], regular?: boolean): boolean;
|
||||
/** 随机获取范围内的整数 */
|
||||
randNum(Min: number, Max: number): number;
|
||||
/** 获取数组最后一个值(无则返回 false) */
|
||||
last(array: any[]): any;
|
||||
/** 字符串是否包含(lower 为 true 时区分大小写) */
|
||||
strExists(string: any, find: any, lower?: boolean): boolean;
|
||||
/** 字符串是否左边包含 */
|
||||
leftExists(string: any, find: any, lower?: boolean): boolean;
|
||||
/** 删除左边字符串 */
|
||||
leftDelete(string: any, find: any, lower?: boolean): string;
|
||||
/** 字符串是否右边包含 */
|
||||
rightExists(string: any, find: any, lower?: boolean): boolean;
|
||||
/** 删除右边字符串 */
|
||||
rightDelete(string: any, find: any, lower?: boolean): string;
|
||||
/** 取字符串中间 */
|
||||
getMiddle(string: any, start?: string | null, end?: string | null): string;
|
||||
/** 截取字符串 */
|
||||
subString(string: any, start: number, end?: number): string;
|
||||
/** 随机字符(默认 32 位) */
|
||||
randomString(len?: number): string;
|
||||
/** 判断是否有值(enhanced 为 true 时空数组/空对象视为无) */
|
||||
isHave(val: any, enhanced?: boolean): boolean;
|
||||
/** 判断是否为真(true/1/"true"/"1") */
|
||||
isTrue(value: any): boolean;
|
||||
/** 相当于 intval(fixed 指定小数位时返回字符串) */
|
||||
runNum(str: any, fixed?: number | string | null): number | string;
|
||||
/** 补零 */
|
||||
zeroFill(str: any, length: number, after?: boolean): string;
|
||||
/** 检测手机号码格式 */
|
||||
isMobile(str: any): boolean;
|
||||
/** 检测邮箱地址格式 */
|
||||
isEmail(email: any): boolean;
|
||||
/** 根据两点间的经纬度计算距离(米,字符串) */
|
||||
getDistance(lng1: number, lat1: number, lng2: number, lat2: number): string;
|
||||
/** 设置网页标题 */
|
||||
setTile(title: string): void;
|
||||
/** 克隆对象 */
|
||||
cloneJSON<T>(value: T, useParse?: boolean): T;
|
||||
/** 将一个 JSON 字符串转换为对象(已try) */
|
||||
jsonParse(str: any, defaultVal?: any): any;
|
||||
/** 将 JavaScript 值转换为 JSON 字符串(已try) */
|
||||
jsonStringify(json: any, defaultVal?: string): string;
|
||||
/** 监听对象尺寸发生改变 */
|
||||
resize(obj: any, callback?: () => void): void;
|
||||
/** 获取屏幕方向 */
|
||||
screenOrientation(): "landscape" | "portrait";
|
||||
/** 是否IOS */
|
||||
isIos(): boolean;
|
||||
/** 是否iPad */
|
||||
isIpad(): boolean;
|
||||
/** 是否安卓 */
|
||||
isAndroid(): boolean;
|
||||
/** 是否微信 */
|
||||
isWeixin(): boolean;
|
||||
/** 是否Chrome */
|
||||
isChrome(): boolean;
|
||||
/** 是否桌面端 */
|
||||
isDesktop(): boolean;
|
||||
/** 获取对象(支持 a.b.c 路径取值) */
|
||||
getObject(obj: any, keys: string | Array<string | number>, defaultValue?: any): any;
|
||||
/** 统计数组或对象长度 */
|
||||
count(obj: any): number;
|
||||
/** 获取文本长度 */
|
||||
stringLength(string: any): number;
|
||||
/** 获取数组长度(处理数组不存在) */
|
||||
arrayLength(array: any): number;
|
||||
/** 将数组或对象内容部分拼成字符串 */
|
||||
objImplode(obj: any): string;
|
||||
/** 指定键获取url参数(不传 key 返回全部) */
|
||||
urlParameter(key?: string): any;
|
||||
/** 获取所有url参数 */
|
||||
urlParameterAll(): Record<string, string>;
|
||||
/** 删除地址中的参数 */
|
||||
removeURLParameter(url: string, keys: string | string[]): string;
|
||||
/** 连接加上参数 */
|
||||
urlAddParams(url: string, params: Record<string, any>): string;
|
||||
/** 替换url中的hash(只传一个参数时视为 path,url 默认当前页面) */
|
||||
urlReplaceHash(url: string, path?: string): string;
|
||||
/** 刷新当前地址 */
|
||||
reloadUrl(): void;
|
||||
/** 链接字符串(第一个参数为连接符) */
|
||||
stringConnect(...value: any[]): string;
|
||||
/** 判断两个对象是否相等 */
|
||||
objEquals(x: any, y: any): boolean;
|
||||
/** 输入框内插入文本 */
|
||||
insert2Input(object: any, content: string): void;
|
||||
/** 输入框数字限制 */
|
||||
inputNumberLimit(object: any, min?: number | null, max?: number | null): void;
|
||||
/** iOS上虚拟键盘引起的触控错位修正 */
|
||||
iOSKeyboardFixer(): void;
|
||||
/** 动态加载js文件 */
|
||||
loadScript(url: string): Promise<boolean>;
|
||||
/** 按顺序动态加载多个js文件 */
|
||||
loadScriptS(urls: string[]): Promise<void>;
|
||||
/** 动态加载css文件 */
|
||||
loadCss(url: string): Promise<boolean>;
|
||||
/** 按顺序动态加载多个css文件 */
|
||||
loadCssS(urls: string[]): Promise<void>;
|
||||
/** 动态加载iframe */
|
||||
loadIframe(url: string, loadedRemove?: number): Promise<boolean>;
|
||||
/** 按顺序动态加载多个iframe */
|
||||
loadIframes(urls: string[]): Promise<void>;
|
||||
/** 字节转换(如 1024 -> "1 KB") */
|
||||
bytesToSize(bytes: number): string;
|
||||
/** html代码转义 */
|
||||
html2Escape(sHtml: string): string;
|
||||
/** 正则提取域名 */
|
||||
getDomain(weburl: any): string;
|
||||
/** 提取 URL 协议 */
|
||||
getProtocol(weburl: any): string;
|
||||
/** 滚动到View */
|
||||
scrollToView(element: Element | null, options?: boolean | Record<string, any>): void;
|
||||
/** 按需滚动到View */
|
||||
scrollIntoViewIfNeeded(element?: Element | null, smooth?: boolean): void;
|
||||
/** 给元素添加一个class,过指定时间之后再去除这个class */
|
||||
addClassWithTimeout(element: Element | null, className: string, duration: number): void;
|
||||
/** 滚动到元素并抖动 */
|
||||
scrollIntoAndShake(element: Element | Element[] | null, viewIfNeeded?: boolean): void;
|
||||
/** 等比缩放尺寸 */
|
||||
scaleToScale(width: number, height: number, maxW: number, maxH?: number): { width: number; height: number };
|
||||
/** 阻止滑动穿透 */
|
||||
scrollPreventThrough(el: HTMLElement | null): void;
|
||||
/** 获取元素属性 */
|
||||
getAttr(el: Element | null, attrName: string, def?: string): string | null;
|
||||
/** 排序JSON对象 */
|
||||
sortObject(obj: Record<string, any>, ignore?: string[]): Record<string, any>;
|
||||
/** 从HTML中提取图片参数 */
|
||||
extractImageParameter(imgTag: string): DooTaskImageParameter;
|
||||
/** 从HTML中提取所有图片参数 */
|
||||
extractImageParameterAll(html: string): DooTaskImageParameter[];
|
||||
/** 增强版的字符串截取(超长自动加后缀) */
|
||||
cutString(str: string, length: number, start?: number, suffix?: string): string;
|
||||
/** 获取两个数组后面的交集 */
|
||||
getLastSameElements(arr1: any[], arr2: any[]): any[];
|
||||
/** 查找元素并在失败时重试 */
|
||||
findElementWithRetry(findElementFn: () => any, maxAttempts?: number, delayMs?: number): Promise<any>;
|
||||
/** 轮询等待条件满足 */
|
||||
waitForCondition(conditionFn: () => boolean, intervalMs?: number, timeoutMs?: number): Promise<boolean>;
|
||||
/** 执行指定次数的定时任务,返回取消函数 */
|
||||
repeatWithCount(fn: (count: number) => boolean | void, delay: number, interval?: number, times?: number): () => void;
|
||||
/** 通过URL生成base64图片 */
|
||||
generateBase64Image(url: string, quality?: number, maxWidth?: number, maxHeight?: number): Promise<string>;
|
||||
/** 是否全屏(根据尺寸对比) */
|
||||
isFullScreen(): boolean;
|
||||
|
||||
/* =========================================================================
|
||||
* common.js —— localForage(IndexedDB)
|
||||
* ========================================================================= */
|
||||
|
||||
/** 测试 IndexedDB 是否可用 */
|
||||
IDBTest(): Promise<boolean>;
|
||||
/** 延迟保存(防抖,默认 100ms) */
|
||||
IDBSave(key: string, value: any, delay?: number): void;
|
||||
/** 删除缓存 */
|
||||
IDBDel(key: string): Promise<void>;
|
||||
/** 设置缓存 */
|
||||
IDBSet(key: string, value: any): Promise<any>;
|
||||
/** 删除缓存(同 IDBDel) */
|
||||
IDBRemove(key: string): Promise<void>;
|
||||
/** 清除缓存(可指定保留的 key) */
|
||||
IDBClear(keysToKeep?: string[]): Promise<void>;
|
||||
/** 获取缓存值 */
|
||||
IDBValue(key: string): Promise<any>;
|
||||
/** 获取缓存值(字符串) */
|
||||
IDBString(key: string, def?: string): Promise<string | number>;
|
||||
/** 获取缓存值(整数) */
|
||||
IDBInt(key: string, def?: number): Promise<number>;
|
||||
/** 获取缓存值(布尔) */
|
||||
IDBBoolean(key: string, def?: boolean): Promise<boolean>;
|
||||
/** 获取缓存值(数组) */
|
||||
IDBArray(key: string, def?: any[]): Promise<any[]>;
|
||||
/** 获取缓存值(对象) */
|
||||
IDBJson(key: string, def?: Record<string, any>): Promise<Record<string, any>>;
|
||||
|
||||
/* =========================================================================
|
||||
* common.js —— localStorage
|
||||
* ========================================================================= */
|
||||
|
||||
/** 设置本地存储 */
|
||||
setStorage(key: string, value: any): void;
|
||||
/** 获取本地存储值 */
|
||||
getStorageValue(key: string): any;
|
||||
/** 获取本地存储值(字符串) */
|
||||
getStorageString(key: string, def?: string): string | number;
|
||||
/** 获取本地存储值(整数) */
|
||||
getStorageInt(key: string, def?: number): number;
|
||||
/** 获取本地存储值(布尔) */
|
||||
getStorageBoolean(key: string, def?: boolean): boolean;
|
||||
/** 获取本地存储值(数组) */
|
||||
getStorageArray(key: string, def?: any[]): any[];
|
||||
/** 获取本地存储值(对象) */
|
||||
getStorageJson(key: string, def?: Record<string, any>): Record<string, any>;
|
||||
/** 本地存储是否存在 */
|
||||
existsStorage(key: string): boolean;
|
||||
|
||||
/* =========================================================================
|
||||
* common.js —— sessionStorage
|
||||
* ========================================================================= */
|
||||
|
||||
/** 设置会话存储 */
|
||||
setSessionStorage(key: string, value: any): void;
|
||||
/** 获取会话存储值 */
|
||||
getSessionStorageValue(key: string): any;
|
||||
/** 获取会话存储值(字符串) */
|
||||
getSessionStorageString(key: string, def?: string): string | number;
|
||||
/** 获取会话存储值(整数) */
|
||||
getSessionStorageInt(key: string, def?: number): number;
|
||||
|
||||
/* =========================================================================
|
||||
* common.js —— ihttp / ajaxc
|
||||
* ========================================================================= */
|
||||
|
||||
/** 序列化对象为查询字符串 */
|
||||
serializeObject(obj: any, parents?: string[]): string;
|
||||
/** 全局 Ajax 配置 */
|
||||
globalAjaxOptions: Record<string, any>;
|
||||
/** 设置全局 Ajax 配置 */
|
||||
ajaxSetup(options: Record<string, any>): void;
|
||||
/** XHR 请求(jQuery.ajax 风格,JSONP 时无返回值) */
|
||||
ihttp(options: Record<string, any>): XMLHttpRequest | void;
|
||||
/** Ajax 请求封装(带请求列表管理,可用 ajaxcCancel 取消) */
|
||||
ajaxc(params: DooTaskAjaxcParams): void | false;
|
||||
/** 取消 ajaxc 请求,返回取消的数量 */
|
||||
ajaxcCancel(requestId: string | number): number;
|
||||
|
||||
/* =========================================================================
|
||||
* common.js —— time / sort
|
||||
* ========================================================================= */
|
||||
|
||||
/** 时间对象(dayjs 实例,自动识别 10/13 位时间戳;返回 any 以避免引入 dayjs 类型依赖) */
|
||||
dayjs(v?: any): any;
|
||||
/** 时间对象(减去时区差,dayjs 实例) */
|
||||
daytz(v?: any): any;
|
||||
/** 更新时区,返回时区差(小时) */
|
||||
updateTimezone(tz?: string): number;
|
||||
/** 当前时区名称 */
|
||||
timezoneName: string | null;
|
||||
/** 时区差(小时) */
|
||||
timezoneDifference: number;
|
||||
/** 对象中有Date格式的转成指定格式(支持 dayjs、Date、string,递归处理对象/数组) */
|
||||
newDateString(value: any, format?: string, key?: string | null): any;
|
||||
/** 对象中有Date格式的转成时间戳(递归处理对象/数组) */
|
||||
newTimestamp(value: any): any;
|
||||
/** 判断是否是日期格式(YYYY-MM-DD[ HH[:mm[:ss]]]) */
|
||||
isDateString(value: any): boolean;
|
||||
/** 秒数倒计时,格式:00:00:00, 00:00, 0s */
|
||||
secondsToTime(s: number): string;
|
||||
/** 格式化时间(本地时间自动减去时区差) */
|
||||
timeFormat(date: any): string;
|
||||
/** 倒计时(开始时间自动减去时区差) */
|
||||
countDownFormat(s: any, e: any): string;
|
||||
/** 计算排序值(日期格式) */
|
||||
sortDay(v1: any, v2: any): number;
|
||||
/** 计算排序值(数字格式) */
|
||||
sortFloat(v1: any, v2: any): number;
|
||||
|
||||
/* =========================================================================
|
||||
* web.js —— 页面专用
|
||||
* ========================================================================= */
|
||||
|
||||
/** 接口地址(补全为完整 API URL) */
|
||||
apiUrl(str: string): string;
|
||||
/** 主页地址 */
|
||||
mainUrl(str?: string | null): string;
|
||||
/** 获取 mainUrl 的域名 */
|
||||
mainDomain(): string;
|
||||
/** 移除 mainUrl 前缀(忽略 http/https 协议差异,只匹配域名) */
|
||||
removeMainUrlPrefix(url: any): string;
|
||||
/** 服务地址 */
|
||||
originUrl(str: string): string;
|
||||
/** 预览文件地址 */
|
||||
onlinePreviewUrl(name: string, key: string): string;
|
||||
/** 项目配置模板 */
|
||||
projectParameterTemplate(project_id: number | string): {
|
||||
project_id: number | string;
|
||||
menuInit: boolean;
|
||||
menuType: string;
|
||||
chat: boolean;
|
||||
showMy: boolean;
|
||||
showHelp: boolean;
|
||||
showUndone: boolean;
|
||||
showCompleted: boolean;
|
||||
completedTask: boolean;
|
||||
};
|
||||
/** 获取日期选择器的 shortcuts 模板参数 */
|
||||
timeOptionShortcuts(): Array<{ text: string; value(): [Date, Date] }>;
|
||||
/** 对话标签(已完成/已删除/已归档) */
|
||||
dialogTags(dialog: any): Array<{ color: string; text: string }>;
|
||||
/** 对话是否完成(返回 success 标签) */
|
||||
dialogCompleted(dialog: any): { color: string; text: string } | undefined;
|
||||
/** 返回对话未读数量(不含免打扰,但如果免打扰中有@则返回@数量) */
|
||||
getDialogNum(dialog: any): number;
|
||||
/** 返回对话未读数量(containSilence 是否包含免打扰消息) */
|
||||
getDialogUnread(dialog: any, containSilence?: boolean): number;
|
||||
/** 返回对话@提及未读数量 */
|
||||
getDialogMention(dialog: any): number;
|
||||
/** 返回文本信息预览格式 */
|
||||
getMsgTextPreview(msgData: { type?: string; text?: string }, imgClassName?: string | null): string;
|
||||
/** 消息格式化处理(将消息内的RemoteURL换成真实地址) */
|
||||
formatMsgBasic<T>(data: T): T;
|
||||
/** 消息格式化处理(@提及、链接、图片尺寸) */
|
||||
formatTextMsg(text: string, userid: number | string): string;
|
||||
/** 获取文本消息图片 */
|
||||
getTextImagesInfo(text: string): Array<{ src: string; width: number | string; height: number | string }>;
|
||||
/** 合并转发消息标题 */
|
||||
getMergeForwardTitle(msg: any): string;
|
||||
/** 消息简单描述 */
|
||||
getMsgSimpleDesc(data: any, imgClassName?: string | null): string;
|
||||
/** 文件消息简单描述 */
|
||||
fileMsgSimpleDesc(msg: any, imgClassName?: string | null): string;
|
||||
/** 模板消息简单描述 */
|
||||
templateMsgSimpleDesc(msg: any): string;
|
||||
/** 获取文件标题(name.ext) */
|
||||
getFileName(file: { name?: string; ext?: string }): string;
|
||||
/** 是否是doo服务器 */
|
||||
isDooServer(): boolean;
|
||||
/** 缩略图还原 */
|
||||
thumbRestore(url: any): string;
|
||||
/** 拖拽或粘贴的数据是否包含文件夹 */
|
||||
dataHasFolder(data: { items?: any }): boolean;
|
||||
/** 图片尺寸比例超出处理(裁剪 + 等比缩放) */
|
||||
imageRatioHandle(params: DooTaskImageRatioParams): DooTaskImageRatioParams;
|
||||
/** 判断图片地址是否满足比例缩放 */
|
||||
imageRatioJudge(url: string): boolean;
|
||||
/** 图片尺寸比例超出(返回超出时的 ratio,否则 0) */
|
||||
imageRatioExceed(width: number, height: number, ratio: number, float?: number): number;
|
||||
/** 去除html内容中无效的部分 */
|
||||
filterInvalidLine(content: any): string;
|
||||
/** 加载 VConsole 日志组件(key: 'log.o' 开 / 'log.c' 关) */
|
||||
loadVConsole(key?: string): boolean | void;
|
||||
/** 提取工作报告中的时间 */
|
||||
reportExtractTime(text: string): string;
|
||||
/** 根据十六进制颜色生成通用 CSS 变量样式 */
|
||||
generateColorVarStyle(hexColor: string, levels?: number[], prefix?: string, styles?: Record<string, string> | null): Record<string, string> | null;
|
||||
/** 转换工作流状态 */
|
||||
convertWorkflow(item: string | { flow_item_name?: string; complete_at?: any }): { status: string | null; name: string; color: string | null };
|
||||
|
||||
/* =========================================================================
|
||||
* web.js —— iviewui assist(弹窗/提示/通知)
|
||||
* 注意:modal/message/notice 系列内部自动 $L 翻译,调用方勿再包 $L
|
||||
* (仅当 config.language === false 时由调用方自行处理翻译)。
|
||||
* ========================================================================= */
|
||||
|
||||
/** 弹窗配置规范化(内部自动 $L 翻译 title/content/okText/cancelText,调用方勿再包 $L) */
|
||||
modalConfig(config?: string | DooTaskModalConfig): DooTaskModalConfig;
|
||||
/** 弹窗文案翻译辅助(language === false 时翻译,否则原样返回交给 modalConfig 统一翻译) */
|
||||
modalTranslation(title: string, language?: boolean): string;
|
||||
/** 输入弹窗(内部自动 $L 翻译,调用方勿再包 $L;millisecond 为延迟弹出毫秒数) */
|
||||
modalInput(config: string | DooTaskModalConfig, millisecond?: number): void;
|
||||
/** 确认弹窗(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
modalConfirm(config: string | DooTaskModalConfig | false, millisecond?: number): void;
|
||||
/** 成功弹窗(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
modalSuccess(config: string | DooTaskModalConfig | false, millisecond?: number): void;
|
||||
/** 信息弹窗(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
modalInfo(config: string | DooTaskModalConfig | false, millisecond?: number): void;
|
||||
/** 警告弹窗(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
modalWarning(config: string | DooTaskModalConfig | false, millisecond?: number): void;
|
||||
/** 错误弹窗(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
modalError(config: string | DooTaskModalConfig | false, millisecond?: number): void;
|
||||
/** alert 弹窗(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
modalAlert(msg: string | false): void;
|
||||
/** 成功提示(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
messageSuccess(msg: string): void;
|
||||
/** 信息提示(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
messageInfo(msg: string): void;
|
||||
/** 警告提示(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
messageWarning(msg: string | false): void;
|
||||
/** 错误提示(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
messageError(msg: string | false): void;
|
||||
/** 通知配置规范化(内部自动 $L 翻译 title/desc,调用方勿再包 $L) */
|
||||
noticeConfig(config?: string | DooTaskNoticeConfig): DooTaskNoticeConfig;
|
||||
/** 成功通知(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
noticeSuccess(config: string | DooTaskNoticeConfig | false): void;
|
||||
/** 警告通知(内部自动 $L 翻译,调用方勿再包 $L) */
|
||||
noticeWarning(config: string | DooTaskNoticeConfig | false): void;
|
||||
/** 错误通知(内部自动 $L 翻译,调用方勿再包 $L;字符串默认 duration 6 秒) */
|
||||
noticeError(config: string | DooTaskNoticeConfig | false): void;
|
||||
|
||||
/* =========================================================================
|
||||
* web.js —— dark 暗黑模式
|
||||
* ========================================================================= */
|
||||
|
||||
/** 暗黑模式工具对象 */
|
||||
dark: DooTaskDark;
|
||||
|
||||
/* =========================================================================
|
||||
* eeui.js —— EEUI App 专用
|
||||
* ========================================================================= */
|
||||
|
||||
/** 获取eeui模块 */
|
||||
eeuiModule(name?: string): any;
|
||||
/** 获取eeui模块(Promise) */
|
||||
eeuiModulePromise(name?: string): Promise<any>;
|
||||
/** 获取eeui版本号 */
|
||||
eeuiAppVersion(): string | undefined;
|
||||
/** 获取本地软件版本号 */
|
||||
eeuiAppLocalVersion(): string | undefined;
|
||||
/** Alert 弹窗 */
|
||||
eeuiAppAlert(object: any, callback?: (result: any) => void): void;
|
||||
/** Toast 提示 */
|
||||
eeuiAppToast(object: any): void;
|
||||
/** 相对地址基于当前地址补全 */
|
||||
eeuiAppRewriteUrl(val: string): string | undefined;
|
||||
/** 获取页面信息 */
|
||||
eeuiAppGetPageInfo(pageName?: string): any;
|
||||
/** 打开app新页面 */
|
||||
eeuiAppOpenPage(object: Record<string, any>, callback?: (result: any) => void): void;
|
||||
/** 使用系统浏览器打开网页 */
|
||||
eeuiAppOpenWeb(url: string): void;
|
||||
/** 拦截返回按键事件(仅支持android、iOS无效) */
|
||||
eeuiAppSetPageBackPressed(object: any, callback?: (result: any) => void): void;
|
||||
/** 返回手机桌面 */
|
||||
eeuiAppGoDesktop(): void;
|
||||
/** 打开屏幕常亮 */
|
||||
eeuiAppKeepScreenOn(): void;
|
||||
/** 关闭屏幕常亮 */
|
||||
eeuiAppKeepScreenOff(): void;
|
||||
/** 隐藏软键盘 */
|
||||
eeuiAppKeyboardHide(): void;
|
||||
/** 给app发送消息 */
|
||||
eeuiAppSendMessage(object: any): void;
|
||||
/** 设置浏览器地址 */
|
||||
eeuiAppSetUrl(url: string): void;
|
||||
/** 生成webview快照 */
|
||||
eeuiAppGetWebviewSnapshot(callback: (result: any) => void): void;
|
||||
/** 显示webview快照 */
|
||||
eeuiAppShowWebviewSnapshot(): void;
|
||||
/** 隐藏webview快照 */
|
||||
eeuiAppHideWebviewSnapshot(): void;
|
||||
/** 扫码(成功时回调扫码文本) */
|
||||
eeuiAppScan(callback: (text: string) => void): void;
|
||||
/** 检查更新 */
|
||||
eeuiAppCheckUpdate(): void;
|
||||
/** 获取主题名称 light|dark */
|
||||
eeuiAppGetThemeName(): string | undefined;
|
||||
/** 判断软键盘是否可见 */
|
||||
eeuiAppKeyboardStatus(): boolean | undefined;
|
||||
/** 设置全局变量 */
|
||||
eeuiAppSetVariate(key: string, value: any): void;
|
||||
/** 获取全局变量 */
|
||||
eeuiAppGetVariate(key: string, defaultVal?: any): any;
|
||||
/** 设置缓存数据 */
|
||||
eeuiAppSetCachesString(key: string, value: string, expired?: number): void;
|
||||
/** 获取缓存数据 */
|
||||
eeuiAppGetCachesString(key: string, defaultVal?: string): string | undefined;
|
||||
/** 是否长按内容震动(仅支持android、iOS无效) */
|
||||
eeuiAppSetHapticBackEnabled(val: boolean): void;
|
||||
/** 禁止长按选择(仅支持android、iOS无效;传毫秒数则临时禁止) */
|
||||
eeuiAppSetDisabledUserLongClickSelect(val: boolean | number | string): void;
|
||||
/** 复制文本 */
|
||||
eeuiAppCopyText(text: string): void;
|
||||
/** 设置是否禁止滚动 */
|
||||
eeuiAppSetScrollDisabled(disabled: boolean): void;
|
||||
/** 设置应用程序级别的摇动撤销(仅支持iOS、android无效) */
|
||||
eeuiAppShakeToEditEnabled(enabled: boolean): void;
|
||||
/** 获取最新一张照片 */
|
||||
eeuiAppGetLatestPhoto(expiration?: number, timeout?: number): Promise<any>;
|
||||
/** 上传照片(params 参数:{url,data,headers,path,fieldName,onReady?}) */
|
||||
eeuiAppUploadPhoto(params: Record<string, any>, timeout?: number): Promise<any>;
|
||||
/** 取消上传照片 */
|
||||
eeuiAppCancelUploadPhoto(id: any): Promise<any>;
|
||||
/** 获取导航栏和状态栏高度 */
|
||||
eeuiAppGetSafeAreaInsets(): Promise<any>;
|
||||
/** 获取当前语言(zh -> zh-Hans 等映射) */
|
||||
eeuiAppConvertLanguage(): string;
|
||||
/** 获取设备信息 */
|
||||
eeuiAppGetDeviceInfo(): Promise<any>;
|
||||
/** 判断是否窗口化 */
|
||||
eeuiAppIsWindowed(): Promise<boolean>;
|
||||
}
|
||||
|
||||
/** DooTask 全局工具对象(jQuery 实例 + $.extend 扩展方法) */
|
||||
declare const $A: DooTaskGlobal;
|
||||
|
||||
/**
|
||||
* 翻译函数(language/index.js switchLanguage)。
|
||||
* 动态值用 (*) 占位:$L('共(*)条', n);禁止拼接翻译。
|
||||
*/
|
||||
declare function $L(text: string, ...args: Array<string | number>): string;
|
||||
|
||||
interface Window {
|
||||
$A: DooTaskGlobal;
|
||||
$L: typeof $L;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user