diff --git a/app/Http/Controllers/IndexController.php b/app/Http/Controllers/IndexController.php index a63e6cac6..72538b41f 100755 --- a/app/Http/Controllers/IndexController.php +++ b/app/Http/Controllers/IndexController.php @@ -4,12 +4,14 @@ namespace App\Http\Controllers; use App\Module\Base; use App\Module\Ihttp; +use App\Module\RandomColor; use App\Tasks\AutoArchivedTask; use App\Tasks\DeleteTmpTask; use App\Tasks\EmailNoticeTask; use Arr; use Cache; use Hhxsv5\LaravelS\Swoole\Task\Task; +use LasseRafn\InitialAvatarGenerator\InitialAvatar; use Redirect; use Request; @@ -123,6 +125,39 @@ class IndexController extends InvokeController return $array; } + /** + * 头像 + * @return \Psr\Http\Message\StreamInterface + */ + public function avatar() + { + $name = Request::input('name', 'H'); + $size = Request::input('size', 128); + $color = Request::input('color'); + $background = Request::input('background'); + // + if (preg_match('/^[\x{4e00}-\x{9fa5}]+$/u', $name)) { + $name = mb_substr($name, mb_strlen($name) - 2); + } + if (empty($color)) { + $color = '#ffffff'; + $cacheKey = "avatarBackgroundColor::" . md5($name); + $background = Cache::rememberForever($cacheKey, function() { + return RandomColor::one(['luminosity' => 'dark']); + }); + } + // + $avatar = new InitialAvatar(); + return $avatar->name($name) + ->size($size) + ->color($color) + ->background($background) + ->fontSize(0.35) + ->autoFont() + ->generate() + ->stream('png', 100); + } + /** * 接口文档 * @return \Illuminate\Http\RedirectResponse diff --git a/app/Models/User.php b/app/Models/User.php index 703a99e74..2d143c6a8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -67,6 +67,8 @@ class User extends AbstractModel 'updated_at', ]; + protected $defaultAvatarMode = 'auto'; // auto自动生成,system系统默认 + /** * 更新数据校验 * @param array $param @@ -97,11 +99,17 @@ class User extends AbstractModel */ public function getUserimgAttribute($value) { - if ($value) { + if ($value && !str_starts_with($value, 'images/avatar/default')) { return Base::fillUrl($value); } - $name = ($this->userid - 1) % 21 + 1; - return url("images/avatar/default_{$name}.png"); + if ($this->defaultAvatarMode === 'auto') { + // 自动生成头像 + return url("avatar?name=" . urlencode($this->nickname)); + } else { + // 系统默认头像 + $name = ($this->userid - 1) % 21 + 1; + return url("images/avatar/default_{$name}.png"); + } } /** diff --git a/app/Module/RandomColor.php b/app/Module/RandomColor.php new file mode 100644 index 000000000..ff5eb98cd --- /dev/null +++ b/app/Module/RandomColor.php @@ -0,0 +1,390 @@ += 0) + { + $ranges[] = array($hue, $hue); + } + } + } + } + + if (($l = count($ranges)) === 0) + { + return array(0, 360); + } + else if ($l === 1) + { + return $ranges[0]; + } + else + { + return $ranges[self::_rand(array(0, $l-1), $options)]; + } + } + + static private function _getMinimumBrightness($h, $s) + { + $colorInfo = self::_getColorInfo($h); + $bounds = $colorInfo['bounds']; + + for ($i = 0, $l = count($bounds); $i < $l - 1; $i++) + { + $s1 = $bounds[$i][0]; + $v1 = $bounds[$i][1]; + $s2 = $bounds[$i+1][0]; + $v2 = $bounds[$i+1][1]; + + if ($s >= $s1 && $s <= $s2) + { + $m = ($v2 - $v1) / ($s2 - $s1); + $b = $v1 - $m * $s1; + return round($m * $s + $b); + } + } + + return 0; + } + + static private function _getColorInfo($h) + { + // Maps red colors to make picking hue easier + if ($h >= 334 && $h <= 360) + { + $h-= 360; + } + + foreach (self::$dictionary as $color) + { + if ($color['h'] !== null && $h >= $color['h'][0] && $h <= $color['h'][1]) + { + return $color; + } + } + } + + static private function _rand($bounds, $options) + { + if (isset($options['prng'])) + { + return $options['prng']($bounds[0], $bounds[1]); + } + else + { + return mt_rand($bounds[0], $bounds[1]); + } + } + + static public function hsv2hex($hsv) + { + $rgb = self::hsv2rgb($hsv); + $hex = '#'; + + foreach ($rgb as $c) + { + $hex.= str_pad(dechex($c), 2, '0', STR_PAD_LEFT); + } + + return $hex; + } + + static public function hsv2hsl($hsv) + { + extract($hsv); + + $s/= 100; + $v/= 100; + $k = (2-$s)*$v; + + return array( + 'h' => $h, + 's' => round($s*$v / ($k < 1 ? $k : 2-$k), 4) * 100, + 'l' => $k/2 * 100, + ); + } + + static public function hsv2rgb($hsv) + { + extract($hsv); + + $h/= 360; + $s/= 100; + $v/= 100; + + $i = floor($h * 6); + $f = $h * 6 - $i; + + $m = $v * (1 - $s); + $n = $v * (1 - $s * $f); + $k = $v * (1 - $s * (1 - $f)); + + $r = 1; + $g = 1; + $b = 1; + + switch ($i) + { + case 0: + list($r,$g,$b) = array($v,$k,$m); + break; + case 1: + list($r,$g,$b) = array($n,$v,$m); + break; + case 2: + list($r,$g,$b) = array($m,$v,$k); + break; + case 3: + list($r,$g,$b) = array($m,$n,$v); + break; + case 4: + list($r,$g,$b) = array($k,$m,$v); + break; + case 5: + case 6: + list($r,$g,$b) = array($v,$m,$n); + break; + } + + return array( + 'r' => floor($r*255), + 'g' => floor($g*255), + 'b' => floor($b*255), + ); + } +} + +/* + * h=hueRange + * s=saturationRange : bounds[0][0] ; bounds[-][0] + */ +RandomColor::$dictionary = array( + 'monochrome' => array( + 'bounds' => array(array(0,0), array(100,0)), + 'h' => NULL, + 's' => array(0,100) + ), + 'red' => array( + 'bounds' => array(array(20,100),array(30,92),array(40,89),array(50,85),array(60,78),array(70,70),array(80,60),array(90,55),array(100,50)), + 'h' => array(-26,18), + 's' => array(20,100) + ), + 'orange' => array( + 'bounds' => array(array(20,100),array(30,93),array(40,88),array(50,86),array(60,85),array(70,70),array(100,70)), + 'h' => array(19,46), + 's' => array(20,100) + ), + 'yellow' => array( + 'bounds' => array(array(25,100),array(40,94),array(50,89),array(60,86),array(70,84),array(80,82),array(90,80),array(100,75)), + 'h' => array(47,62), + 's' => array(25,100) + ), + 'green' => array( + 'bounds' => array(array(30,100),array(40,90),array(50,85),array(60,81),array(70,74),array(80,64),array(90,50),array(100,40)), + 'h' => array(63,178), + 's' => array(30,100) + ), + 'blue' => array( + 'bounds' => array(array(20,100),array(30,86),array(40,80),array(50,74),array(60,60),array(70,52),array(80,44),array(90,39),array(100,35)), + 'h' => array(179,257), + 's' => array(20,100) + ), + 'purple' => array( + 'bounds' => array(array(20,100),array(30,87),array(40,79),array(50,70),array(60,65),array(70,59),array(80,52),array(90,45),array(100,42)), + 'h' => array(258,282), + 's' => array(20,100) + ), + 'pink' => array( + 'bounds' => array(array(20,100),array(30,90),array(40,86),array(60,84),array(80,80),array(90,75),array(100,73)), + 'h' => array(283,334), + 's' => array(20,100) + ) +); diff --git a/composer.json b/composer.json index 6c5ef6f22..ea43819bf 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,7 @@ "hedeqiang/umeng": "^2.1", "laravel/framework": "^v8.48.1", "laravel/tinker": "^v2.6.1", + "lasserafn/php-initial-avatar-generator": "^4.2", "maatwebsite/excel": "^3.1.31", "madnest/madzipper": "^v1.1.0", "mews/captcha": "^3.2.6", diff --git a/composer.lock b/composer.lock index 2f09aa2f4..98295e0dc 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "899b6938adcec6d779c54a3005df68a2", + "content-hash": "9cb3ad18f25c6607bf31539b459e2dda", "packages": [ { "name": "asm89/stack-cors", @@ -1646,6 +1646,169 @@ }, "time": "2022-03-23T12:38:24+00:00" }, + { + "name": "lasserafn/php-initial-avatar-generator", + "version": "4.2.1", + "source": { + "type": "git", + "url": "https://github.com/LasseRafn/php-initial-avatar-generator.git", + "reference": "49d0b10cc8819af831e0f6fb1056a7d5ed9512d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/LasseRafn/php-initial-avatar-generator/zipball/49d0b10cc8819af831e0f6fb1056a7d5ed9512d0", + "reference": "49d0b10cc8819af831e0f6fb1056a7d5ed9512d0", + "shasum": "" + }, + "require": { + "ext-json": "*", + "intervention/image": "^2.3", + "lasserafn/php-initials": "^3.0", + "lasserafn/php-string-script-language": "^0.3.0", + "meyfa/php-svg": "^0.9.0", + "overtrue/pinyin": "^4.0", + "php": "^7.0|^7.1|^7.2|^7.3|^7.4|^8.0" + }, + "require-dev": { + "doctrine/instantiator": "1.0.*", + "phpunit/phpunit": "^6.5", + "satooshi/php-coveralls": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "LasseRafn\\InitialAvatarGenerator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lasse Rafn", + "email": "lasserafn@gmail.com" + } + ], + "description": "A package to generate avatars with initials for PHP", + "keywords": [ + "Initials", + "avatar", + "image", + "svg" + ], + "support": { + "issues": "https://github.com/LasseRafn/php-initial-avatar-generator/issues", + "source": "https://github.com/LasseRafn/php-initial-avatar-generator/tree/4.2.1" + }, + "funding": [ + { + "url": "https://opencollective.com/ui-avatars", + "type": "open_collective" + } + ], + "time": "2020-12-24T13:12:12+00:00" + }, + { + "name": "lasserafn/php-initials", + "version": "3.1", + "source": { + "type": "git", + "url": "https://github.com/LasseRafn/php-initials.git", + "reference": "d287e1542687390eb68de779949bc0adc49e2d52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/LasseRafn/php-initials/zipball/d287e1542687390eb68de779949bc0adc49e2d52", + "reference": "d287e1542687390eb68de779949bc0adc49e2d52", + "shasum": "" + }, + "require": { + "php": "^5.6|^7.0|^7.1|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7", + "satooshi/php-coveralls": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "LasseRafn\\Initials\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lasse Rafn", + "email": "lasserafn@gmail.com" + } + ], + "description": "A package to generate initials in PHP", + "keywords": [ + "Initials", + "php" + ], + "support": { + "issues": "https://github.com/LasseRafn/php-initials/issues", + "source": "https://github.com/LasseRafn/php-initials/tree/3.1" + }, + "time": "2020-12-24T12:25:51+00:00" + }, + { + "name": "lasserafn/php-string-script-language", + "version": "0.3", + "source": { + "type": "git", + "url": "https://github.com/LasseRafn/php-string-script-language.git", + "reference": "49a09d4a5e38c1e59a2656ac05b601d615c7cddb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/LasseRafn/php-string-script-language/zipball/49a09d4a5e38c1e59a2656ac05b601d615c7cddb", + "reference": "49a09d4a5e38c1e59a2656ac05b601d615c7cddb", + "shasum": "" + }, + "require": { + "php": "^5.6|^7.0|^7.1|^8.0" + }, + "require-dev": { + "doctrine/instantiator": "1.0.5", + "phpunit/phpunit": "^5.6", + "phpunit/phpunit-mock-objects": "3.2.4", + "satooshi/php-coveralls": "^1.0", + "sebastian/exporter": "^1.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "LasseRafn\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lasse Rafn", + "email": "lasserafn@gmail.com" + } + ], + "description": "Detect language/encoding of a string in PHP", + "keywords": [ + "language", + "php", + "string" + ], + "support": { + "issues": "https://github.com/LasseRafn/php-string-script-language/issues", + "source": "https://github.com/LasseRafn/php-string-script-language/tree/0.3" + }, + "time": "2020-12-24T12:43:59+00:00" + }, { "name": "league/commonmark", "version": "2.3.3", @@ -2376,6 +2539,56 @@ }, "time": "2022-02-12T15:58:32+00:00" }, + { + "name": "meyfa/php-svg", + "version": "v0.9.1", + "source": { + "type": "git", + "url": "https://github.com/meyfa/php-svg.git", + "reference": "34401edef1f724898f468f71b85505fbcc8351bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/meyfa/php-svg/zipball/34401edef1f724898f468f71b85505fbcc8351bb", + "reference": "34401edef1f724898f468f71b85505fbcc8351bb", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "ext-simplexml": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "meyfa/phpunit-assert-gd": "^1.1", + "phpunit/phpunit": "^4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "SVG\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabian Meyer", + "homepage": "http://meyfa.net" + } + ], + "description": "Read, edit, write, and render SVG files with PHP", + "homepage": "https://github.com/meyfa/php-svg", + "keywords": [ + "svg" + ], + "support": { + "issues": "https://github.com/meyfa/php-svg/issues", + "source": "https://github.com/meyfa/php-svg/tree/v0.9.1" + }, + "time": "2019-07-30T18:41:25+00:00" + }, { "name": "monolog/monolog", "version": "2.7.0", diff --git a/resources/assets/js/components/UserAvatar.vue b/resources/assets/js/components/UserAvatar.vue index f7d177846..306a832a7 100755 --- a/resources/assets/js/components/UserAvatar.vue +++ b/resources/assets/js/components/UserAvatar.vue @@ -173,7 +173,7 @@ isDefault() { const {userimg} = this.user - return $A.strExists(userimg, '/avatar/default_'); + return $A.strExists(userimg, '/avatar'); }, nickname() { diff --git a/resources/assets/js/pages/manage/setting/personal.vue b/resources/assets/js/pages/manage/setting/personal.vue index 3a502a314..75faac09b 100644 --- a/resources/assets/js/pages/manage/setting/personal.vue +++ b/resources/assets/js/pages/manage/setting/personal.vue @@ -62,7 +62,7 @@ export default { }, initData() { - if (!$A.strExists(this.userInfo.userimg, '/avatar/default_')) { + if (!$A.strExists(this.userInfo.userimg, '/avatar')) { this.$set(this.formData, 'userimg', this.userInfo.userimg); } const nickname = typeof this.userInfo.nickname_original !== "undefined" ? this.userInfo.nickname_original : this.userInfo.nickname;