658 lines
22 KiB
PHP
658 lines
22 KiB
PHP
<?php
|
||
/**
|
||
* BuildAdmin Webman 公共函数
|
||
*/
|
||
|
||
// mb_split 兼容:mbstring 扩展未启用时,Illuminate 的 Str::studly 会报错,用 preg_split 兜底
|
||
if (!function_exists('mb_split')) {
|
||
function mb_split(string $pattern, string $string, int $limit = -1): array
|
||
{
|
||
$result = @preg_split('#' . $pattern . '#u', $string, $limit);
|
||
return $result !== false ? $result : [];
|
||
}
|
||
}
|
||
|
||
use support\Response;
|
||
|
||
if (!function_exists('env')) {
|
||
/**
|
||
* 获取环境变量(兼容 dot 格式如 database.hostname)
|
||
*/
|
||
function env(string $key, mixed $default = null): mixed
|
||
{
|
||
$value = $_ENV[$key] ?? getenv($key);
|
||
if ($value !== false && $value !== null) {
|
||
return $value;
|
||
}
|
||
if (strpos($key, '.') !== false) {
|
||
$parts = explode('.', $key);
|
||
$upper = strtoupper(implode('_', $parts));
|
||
$value = $_ENV[$upper] ?? getenv($upper);
|
||
if ($value !== false && $value !== null) {
|
||
return $value;
|
||
}
|
||
}
|
||
return $default;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('__')) {
|
||
/**
|
||
* 语言翻译(BuildAdmin 兼容)
|
||
*/
|
||
function __(string $name, array $vars = [], string $lang = ''): mixed
|
||
{
|
||
if (is_numeric($name) || !$name) {
|
||
return $name;
|
||
}
|
||
return function_exists('trans') ? trans($name, $vars, null, $lang ?: null) : $name;
|
||
}
|
||
}
|
||
|
||
use Symfony\Component\HttpFoundation\IpUtils;
|
||
|
||
if (!function_exists('get_sys_config')) {
|
||
/**
|
||
* 获取系统配置(从数据库或 config)
|
||
* 需 Config 模型支持,否则从 config 读取
|
||
*/
|
||
function get_sys_config(string $name = '', string $group = '', bool $concise = true): mixed
|
||
{
|
||
if (class_exists(\app\admin\model\Config::class)) {
|
||
$configModel = \app\admin\model\Config::class;
|
||
if ($name) {
|
||
$config = $configModel::cache($name, null, $configModel::$cacheTag)->where('name', $name)->find();
|
||
return $config ? $config['value'] : null;
|
||
}
|
||
if ($group) {
|
||
$temp = $configModel::cache('group' . $group, null, $configModel::$cacheTag)->where('group', $group)->select()->toArray();
|
||
} else {
|
||
$temp = $configModel::cache('sys_config_all', null, $configModel::$cacheTag)->order('weigh desc')->select()->toArray();
|
||
}
|
||
if ($concise) {
|
||
$config = [];
|
||
foreach ($temp as $item) {
|
||
$config[$item['name']] = $item['value'];
|
||
}
|
||
return $config;
|
||
}
|
||
return $temp;
|
||
}
|
||
return config("sys_config.{$name}", null);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('clear_config_cache')) {
|
||
/**
|
||
* 清理配置缓存(Config 写入后调用)
|
||
*/
|
||
function clear_config_cache(): void
|
||
{
|
||
$cachePath = base_path() . DIRECTORY_SEPARATOR . 'runtime' . DIRECTORY_SEPARATOR . 'cache';
|
||
if (!is_dir($cachePath)) {
|
||
return;
|
||
}
|
||
$files = new \RecursiveIteratorIterator(
|
||
new \RecursiveDirectoryIterator($cachePath, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||
\RecursiveIteratorIterator::CHILD_FIRST
|
||
);
|
||
foreach ($files as $file) {
|
||
if ($file->isFile()) {
|
||
@unlink($file->getRealPath());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!function_exists('ip_check')) {
|
||
/**
|
||
* IP 检查
|
||
* @param string|null $ip 要检查的 IP,null 时从 request 获取
|
||
* @param \Webman\Http\Request|null $request
|
||
* @return Response|null 禁止访问时返回 Response,否则 null
|
||
*/
|
||
function ip_check(?string $ip = null, $request = null): ?Response
|
||
{
|
||
if ($ip === null && $request) {
|
||
$ip = $request->getRealIp();
|
||
}
|
||
if (!$ip) {
|
||
return null;
|
||
}
|
||
$noAccess = get_sys_config('no_access_ip');
|
||
$noAccess = !$noAccess ? [] : array_filter(explode("\n", str_replace("\r\n", "\n", (string) $noAccess)));
|
||
if ($noAccess && IpUtils::checkIp($ip, $noAccess)) {
|
||
return response(json_encode(['msg' => 'No permission request']), 403, ['Content-Type' => 'application/json']);
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_auth_token')) {
|
||
/**
|
||
* 获取鉴权 token
|
||
* @param array $names 如 ['ba', 'token']
|
||
* @param \Webman\Http\Request|null $request
|
||
*/
|
||
function get_auth_token(array $names = ['ba', 'token'], $request = null): string
|
||
{
|
||
$request = $request ?? (function_exists('request') ? request() : null);
|
||
if (!$request) {
|
||
return '';
|
||
}
|
||
$separators = [
|
||
'header' => ['', '-'],
|
||
'param' => ['', '-', '_'],
|
||
'server' => ['_'],
|
||
];
|
||
$tokens = [];
|
||
foreach ($separators as $source => $sps) {
|
||
foreach ($sps as $sp) {
|
||
$key = ($source === 'server' ? 'http_' : '') . implode($sp, $names);
|
||
if ($source === 'header') {
|
||
$val = $request->header($key);
|
||
} elseif ($source === 'param') {
|
||
$val = $request->get($key) ?? $request->post($key);
|
||
} else {
|
||
$val = $_SERVER[$key] ?? null;
|
||
}
|
||
if ($val) {
|
||
$tokens[] = $val;
|
||
}
|
||
}
|
||
}
|
||
return $tokens[0] ?? '';
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_controller_path')) {
|
||
/**
|
||
* 从 Request 或路由获取控制器路径(等价于 ThinkPHP controllerPath)
|
||
* 优先从 $request->controller(Webman 路由匹配时设置)解析,否则从 path 解析
|
||
* @param \Webman\Http\Request|null $request
|
||
* @return string|null 如 auth/admin、user/user
|
||
*/
|
||
function get_controller_path($request = null): ?string
|
||
{
|
||
$request = $request ?? (function_exists('request') ? request() : null);
|
||
if (!$request) {
|
||
return null;
|
||
}
|
||
|
||
// 优先从路由匹配的 controller 解析(Webman 在路由匹配后设置)
|
||
$controller = $request->controller ?? null;
|
||
if ($controller && is_string($controller)) {
|
||
foreach (['app\\admin\\controller\\', 'app\\api\\controller\\'] as $prefix) {
|
||
if (str_starts_with($controller, $prefix)) {
|
||
$relative = substr($controller, strlen($prefix));
|
||
$parts = explode('\\', $relative);
|
||
$path = [];
|
||
foreach ($parts as $p) {
|
||
$path[] = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $p));
|
||
}
|
||
return implode('/', $path);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 回退:从 path 解析(如 admin/auth/admin/index -> auth/admin)
|
||
$path = trim($request->path(), '/');
|
||
if (!$path) {
|
||
return null;
|
||
}
|
||
$parts = explode('/', $path);
|
||
if (count($parts) < 2) {
|
||
return $parts[0] ?? null;
|
||
}
|
||
return implode('/', array_slice($parts, 1, -1)) ?: $parts[1];
|
||
}
|
||
}
|
||
|
||
if (!function_exists('action_in_arr')) {
|
||
/**
|
||
* 检测当前方法是否在数组中(用于 noNeedLogin、noNeedPermission)
|
||
* @param array $arr
|
||
* @param string|null $action 当前 action,null 时从 request path 解析
|
||
*/
|
||
function action_in_arr(array $arr, ?string $action = null): bool
|
||
{
|
||
if (!$arr) {
|
||
return false;
|
||
}
|
||
$arr = array_map('strtolower', $arr);
|
||
if (in_array('*', $arr)) {
|
||
return true;
|
||
}
|
||
if ($action === null && function_exists('request')) {
|
||
$req = request();
|
||
$path = trim($req->path(), '/');
|
||
$parts = explode('/', $path);
|
||
$action = $parts[array_key_last($parts)] ?? '';
|
||
}
|
||
return $action ? in_array(strtolower($action), $arr) : false;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('event_trigger')) {
|
||
/**
|
||
* 触发事件(BuildAdmin 兼容,替代 Event::trigger)
|
||
* @param string $event
|
||
* @param mixed ...$args
|
||
*/
|
||
function event_trigger(string $event, mixed ...$args): void
|
||
{
|
||
$listeners = config("events.listen.{$event}", []);
|
||
foreach ($listeners as $listener) {
|
||
try {
|
||
if (is_string($listener) && class_exists($listener)) {
|
||
$instance = new $listener();
|
||
if (method_exists($instance, 'handle')) {
|
||
$instance->handle(...$args);
|
||
}
|
||
} elseif (is_callable($listener)) {
|
||
$listener(...$args);
|
||
}
|
||
} catch (\Throwable $e) {
|
||
if (class_exists(\support\Log::class)) {
|
||
\support\Log::warning("[event_trigger] {$event}: " . $e->getMessage());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!function_exists('set_timezone')) {
|
||
/**
|
||
* 设置时区
|
||
*/
|
||
function set_timezone(?string $timezone = null): void
|
||
{
|
||
$defaultTimezone = config('app.default_timezone', 'Asia/Shanghai');
|
||
$timezone = $timezone ?? get_sys_config('time_zone');
|
||
if ($timezone && $defaultTimezone !== $timezone) {
|
||
date_default_timezone_set($timezone);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!function_exists('encrypt_password')) {
|
||
/**
|
||
* 加密密码(兼容旧版 md5)
|
||
* @deprecated 使用 hash_password 代替
|
||
*/
|
||
function encrypt_password(string $password, string $salt = '', string $encrypt = 'md5'): string
|
||
{
|
||
return $encrypt($encrypt($password) . $salt);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('hash_password')) {
|
||
/**
|
||
* 创建密码散列(hash)
|
||
*/
|
||
function hash_password(string $password): string
|
||
{
|
||
return password_hash($password, PASSWORD_DEFAULT);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('verify_password')) {
|
||
/**
|
||
* 验证密码是否和散列值匹配
|
||
*/
|
||
function verify_password(string $password, string $hash, array $extend = []): bool
|
||
{
|
||
if (str_starts_with($hash, '$') || password_get_info($hash)['algoName'] !== 'unknown') {
|
||
return password_verify($password, $hash);
|
||
}
|
||
return encrypt_password($password, $extend['salt'] ?? '') === $hash;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('full_url')) {
|
||
/**
|
||
* 获取资源完整 url 地址
|
||
*/
|
||
function full_url(string $relativeUrl = '', string|bool $domain = true, string $default = ''): string
|
||
{
|
||
$cdnUrl = config('buildadmin.cdn_url');
|
||
if (!$cdnUrl && function_exists('request')) {
|
||
$req = request();
|
||
$cdnUrl = $req ? '//' . $req->host() : '//localhost';
|
||
} elseif (!$cdnUrl) {
|
||
$cdnUrl = '//localhost';
|
||
}
|
||
|
||
if ($domain === true) {
|
||
$domain = $cdnUrl;
|
||
} elseif ($domain === false) {
|
||
$domain = '';
|
||
}
|
||
|
||
$relativeUrl = $relativeUrl ?: $default;
|
||
if (!$relativeUrl) {
|
||
return $domain;
|
||
}
|
||
|
||
$regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
|
||
if (preg_match('/^http(s)?:\/\//', $relativeUrl) || preg_match($regex, $relativeUrl) || $domain === false) {
|
||
return $relativeUrl;
|
||
}
|
||
|
||
$url = $domain . $relativeUrl;
|
||
$cdnUrlParams = config('buildadmin.cdn_url_params');
|
||
if ($domain === $cdnUrl && $cdnUrlParams) {
|
||
$separator = str_contains($url, '?') ? '&' : '?';
|
||
$url .= $separator . $cdnUrlParams;
|
||
}
|
||
|
||
return $url;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('parse_name')) {
|
||
/**
|
||
* 命名转换(ThinkPHP 兼容)
|
||
* @param string $name 名称
|
||
* @param int $type 0=转小写+下划线 1=驼峰 2=首字母大写驼峰
|
||
* @param string $delimiter 分隔符
|
||
*/
|
||
function parse_name(string $name, int $type = 0, string $delimiter = '_'): string
|
||
{
|
||
if ($type === 0) {
|
||
return strtolower(preg_replace('/([A-Z])/', $delimiter . '$1', lcfirst($name)));
|
||
}
|
||
if ($type === 1) {
|
||
$name = str_replace($delimiter, ' ', $name);
|
||
return lcfirst(str_replace(' ', '', ucwords($name)));
|
||
}
|
||
if ($type === 2) {
|
||
$name = str_replace($delimiter, ' ', $name);
|
||
return str_replace(' ', '', ucwords($name));
|
||
}
|
||
return $name;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('root_path')) {
|
||
/**
|
||
* 根路径(BuildAdmin 兼容,等价于 base_path)
|
||
* 无参数时返回带尾部分隔符的路径,确保 root_path() . 'app' 拼接正确
|
||
* @param string $path 子路径
|
||
*/
|
||
function root_path(string $path = ''): string
|
||
{
|
||
$base = base_path($path);
|
||
if ($path === '' && $base !== '') {
|
||
return rtrim($base, DIRECTORY_SEPARATOR . '/') . DIRECTORY_SEPARATOR;
|
||
}
|
||
return $base;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('app_path')) {
|
||
/**
|
||
* 应用目录路径(Webman 兼容,用于 CRUD Helper 等)
|
||
* @param string $path 子路径
|
||
*/
|
||
function app_path(string $path = ''): string
|
||
{
|
||
$base = rtrim(base_path(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'app';
|
||
return $path ? $base . DIRECTORY_SEPARATOR . ltrim(str_replace('/', DIRECTORY_SEPARATOR, $path), DIRECTORY_SEPARATOR) : $base;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_controller_list')) {
|
||
/**
|
||
* 获取控制器文件列表(递归)
|
||
* @param string $app 应用名,默认 admin
|
||
*/
|
||
function get_controller_list(string $app = 'admin'): array
|
||
{
|
||
$controllerDir = root_path() . 'app' . DIRECTORY_SEPARATOR . $app . DIRECTORY_SEPARATOR . 'controller' . DIRECTORY_SEPARATOR;
|
||
return (class_exists(\ba\Filesystem::class) && is_dir($controllerDir))
|
||
? \ba\Filesystem::getDirFiles($controllerDir)
|
||
: [];
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_route_remark')) {
|
||
/**
|
||
* 获取当前路由后台菜单规则的备注信息
|
||
* 使用控制器 domain 翻译,以支持不同控制器对同一 key(如 Remark lang)的不同翻译
|
||
*/
|
||
function get_route_remark(): string
|
||
{
|
||
$controllerPath = get_controller_path() ?? '';
|
||
$actionName = '';
|
||
if (function_exists('request')) {
|
||
$req = request();
|
||
if ($req) {
|
||
$path = trim($req->path(), '/');
|
||
$parts = explode('/', $path);
|
||
$actionName = $parts[array_key_last($parts)] ?? '';
|
||
}
|
||
}
|
||
$path = str_replace('.', '/', $controllerPath);
|
||
$names = [$path];
|
||
if ($actionName) {
|
||
$names[] = $path . '/' . $actionName;
|
||
}
|
||
$remark = \support\think\Db::name('admin_rule')
|
||
->where('name', 'in', $names)
|
||
->value('remark');
|
||
$remarkStr = (string) ($remark ?? '');
|
||
if (!$remarkStr) {
|
||
return '';
|
||
}
|
||
return function_exists('trans') ? trans($remarkStr, [], $controllerPath ?: null) : $remarkStr;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('keys_to_camel_case')) {
|
||
function keys_to_camel_case(array $array, array $keys = []): array
|
||
{
|
||
$result = [];
|
||
foreach ($array as $key => $value) {
|
||
$camelCaseKey = ($keys && in_array($key, $keys)) ? parse_name($key, 1, '_') : $key;
|
||
if (is_array($value)) {
|
||
$result[$camelCaseKey] = keys_to_camel_case($value);
|
||
} else {
|
||
$result[$camelCaseKey] = $value;
|
||
}
|
||
}
|
||
return $result;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_upload_config')) {
|
||
function get_upload_config($request = null): array
|
||
{
|
||
event_trigger('uploadConfigInit', null);
|
||
$uploadConfig = config('upload', []);
|
||
$uploadConfig['max_size'] = \ba\Filesystem::fileUnitToByte($uploadConfig['max_size'] ?? '10M');
|
||
$request = $request ?? (function_exists('request') ? request() : null);
|
||
$upload = $request && isset($request->upload) ? $request->upload : null;
|
||
if (!$upload) {
|
||
$uploadConfig['mode'] = 'local';
|
||
return $uploadConfig;
|
||
}
|
||
unset($upload['cdn']);
|
||
return array_merge($upload, $uploadConfig);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('filter')) {
|
||
function filter(string $string): string
|
||
{
|
||
$string = trim($string);
|
||
$string = strip_tags($string);
|
||
return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8');
|
||
}
|
||
}
|
||
|
||
if (!function_exists('clean_xss')) {
|
||
function clean_xss(string $string): string
|
||
{
|
||
if (class_exists(\voku\helper\AntiXSS::class)) {
|
||
$antiXss = new \voku\helper\AntiXSS();
|
||
$antiXss->removeEvilAttributes(['style']);
|
||
$antiXss->setReplacement('cleanXss');
|
||
return $antiXss->xss_clean($string);
|
||
}
|
||
return strip_tags($string);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('htmlspecialchars_decode_improve')) {
|
||
function htmlspecialchars_decode_improve(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401): string
|
||
{
|
||
return htmlspecialchars_decode($string, $flags);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_ba_client')) {
|
||
/**
|
||
* 获取请求 BuildAdmin 开源社区的 Guzzle Client(用于云端 CRUD 历史等)
|
||
*/
|
||
function get_ba_client(): \GuzzleHttp\Client
|
||
{
|
||
return new \GuzzleHttp\Client([
|
||
'base_uri' => config('buildadmin.api_url', 'https://api.buildadmin.com'),
|
||
'timeout' => 30,
|
||
'connect_timeout' => 30,
|
||
'verify' => false,
|
||
'http_errors' => false,
|
||
'headers' => [
|
||
'X-REQUESTED-WITH' => 'XMLHttpRequest',
|
||
'User-Agent' => 'BuildAdminClient',
|
||
]
|
||
]);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('str_attr_to_array')) {
|
||
function str_attr_to_array(string $attr): array
|
||
{
|
||
if (!$attr) return [];
|
||
$attr = explode("\n", trim(str_replace("\r\n", "\n", $attr)));
|
||
$attrTemp = [];
|
||
foreach ($attr as $item) {
|
||
$item = explode('=', $item);
|
||
if (isset($item[0]) && isset($item[1])) {
|
||
$attrVal = $item[1];
|
||
if ($item[1] === 'false' || $item[1] === 'true') {
|
||
$attrVal = !($item[1] === 'false');
|
||
} elseif (is_numeric($item[1])) {
|
||
$attrVal = (float)$item[1];
|
||
}
|
||
if (strpos($item[0], '.') !== false) {
|
||
$attrKey = explode('.', $item[0]);
|
||
if (isset($attrKey[0]) && isset($attrKey[1])) {
|
||
$attrTemp[$attrKey[0]][$attrKey[1]] = $attrVal;
|
||
continue;
|
||
}
|
||
}
|
||
$attrTemp[$item[0]] = $attrVal;
|
||
}
|
||
}
|
||
return $attrTemp;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('hsv2rgb')) {
|
||
function hsv2rgb($h, $s, $v): array
|
||
{
|
||
$r = $g = $b = 0;
|
||
$i = floor($h * 6);
|
||
$f = $h * 6 - $i;
|
||
$p = $v * (1 - $s);
|
||
$q = $v * (1 - $f * $s);
|
||
$t = $v * (1 - (1 - $f) * $s);
|
||
switch ($i % 6) {
|
||
case 0: $r = $v; $g = $t; $b = $p; break;
|
||
case 1: $r = $q; $g = $v; $b = $p; break;
|
||
case 2: $r = $p; $g = $v; $b = $t; break;
|
||
case 3: $r = $p; $g = $q; $b = $v; break;
|
||
case 4: $r = $t; $g = $p; $b = $v; break;
|
||
case 5: $r = $v; $g = $p; $b = $q; break;
|
||
}
|
||
return [floor($r * 255), floor($g * 255), floor($b * 255)];
|
||
}
|
||
}
|
||
|
||
if (!function_exists('build_suffix_svg')) {
|
||
function build_suffix_svg(string $suffix = 'file', ?string $background = null): string
|
||
{
|
||
$suffix = mb_substr(strtoupper($suffix), 0, 4);
|
||
$total = unpack('L', hash('adler32', $suffix, true))[1];
|
||
$hue = $total % 360;
|
||
[$r, $g, $b] = hsv2rgb($hue / 360, 0.3, 0.9);
|
||
$background = $background ?: "rgb($r,$g,$b)";
|
||
return '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
|
||
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
|
||
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
|
||
<path style="fill:' . $background . ';" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16 V416z"/>
|
||
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
|
||
<g><text><tspan x="220" y="380" font-size="124" font-family="Verdana, Helvetica, Arial, sans-serif" fill="white" text-anchor="middle">' . $suffix . '</tspan></text></g>
|
||
</svg>';
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_account_verification_type')) {
|
||
/**
|
||
* 获取可用的账户验证方式
|
||
* @return string[] email=电子邮件,mobile=手机短信验证
|
||
*/
|
||
function get_account_verification_type(): array
|
||
{
|
||
$types = [];
|
||
$sysMailConfig = get_sys_config('', 'mail');
|
||
$configured = true;
|
||
if (is_array($sysMailConfig)) {
|
||
foreach ($sysMailConfig as $item) {
|
||
if (!$item) {
|
||
$configured = false;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
$configured = false;
|
||
}
|
||
if ($configured) {
|
||
$types[] = 'email';
|
||
}
|
||
if (class_exists(\app\admin\library\module\Server::class)) {
|
||
$sms = \app\admin\library\module\Server::getIni(\ba\Filesystem::fsFit(root_path() . 'modules/sms/'));
|
||
if ($sms && ($sms['state'] ?? 0) == 1) {
|
||
$types[] = 'mobile';
|
||
}
|
||
}
|
||
return $types;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_area')) {
|
||
function get_area($request = null): array
|
||
{
|
||
$request = $request ?? (function_exists('request') ? request() : null);
|
||
$province = $request ? $request->get('province', '') : '';
|
||
$city = $request ? $request->get('city', '') : '';
|
||
$where = ['pid' => 0, 'level' => 1];
|
||
if ($province !== '') {
|
||
$where['pid'] = $province;
|
||
$where['level'] = 2;
|
||
if ($city !== '') {
|
||
$where['pid'] = $city;
|
||
$where['level'] = 3;
|
||
}
|
||
}
|
||
return \support\think\Db::name('area')
|
||
->where($where)
|
||
->field('id as value,name as label')
|
||
->select()
|
||
->toArray();
|
||
}
|
||
}
|