Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions config/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@
|--------------------------------------------------------------------------
|
| The version string appended to JS/CSS URLs as a cache-busting query
| parameter (e.g. `?v1.0.0`). Defaults to the framework version. Set to
| null/empty to disable the version query entirely.
| parameter (e.g. `?v1.0.0`). When null (env not set), falls back to the
| framework version. Set to an empty string ('') to disable the version
| query entirely.
|
*/
'assets_version' => env('ADMIN_ASSETS_VERSION'),
Expand Down
2 changes: 1 addition & 1 deletion resources/views/form/multipleselect.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
@include('admin::form.select-script')

<script>
$('.{{$selector}}').off('change.select2').on('change.select2', function () {
$('.{{$selector}}').off('change.dcat-msel').on('change.dcat-msel', function () {
$(this).closest('.field').find('input[type="hidden"][name="{{$name}}[]"]').prop('disabled', $(this).val()?.length > 0);
});
</script>
5 changes: 3 additions & 2 deletions src/Console/stubs/config.stub
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ return [
|--------------------------------------------------------------------------
|
| The version string appended to JS/CSS URLs as a cache-busting query
| parameter (e.g. `?v1.0.0`). Defaults to the framework version. Set to
| null/empty to disable the version query entirely.
| parameter (e.g. `?v1.0.0`). When null (env not set), falls back to the
| framework version. Set to an empty string ('') to disable the version
| query entirely.
|
*/
'assets_version' => env('ADMIN_ASSETS_VERSION'),
Expand Down
5 changes: 3 additions & 2 deletions src/Layout/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -689,9 +689,10 @@ public function withVersionQuery($url)
$url .= '?';
}

$assetsVersion = config('admin.assets_version', Admin::VERSION);
// 键存在但为 null(env 未设置)时回退框架版本;显式置空才禁用版本参数
$assetsVersion = config('admin.assets_version') ?? Admin::VERSION;

if (! $assetsVersion) {
if ($assetsVersion === '' || $assetsVersion === false) {
return rtrim($url, '?');
}

Expand Down
47 changes: 41 additions & 6 deletions src/Support/SessionMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

namespace Dcat\Admin\Support;

use Illuminate\Contracts\Support\MessageBag;

/**
* Session 消息对象(兼容 Laravel 10-13,兼容 PHP session.serialization = json/php).
*
* 解决两类问题:
* 1. Laravel 13 中 MessageBag 通过 redirect()->with() 序列化后变为数组的问题
* 2. session.serialization = json 时,protected 属性不会被 json_encode 包含的问题
*
* 同时向后兼容旧契约:外部代码直接 flash 的 MessageBag(实例或其序列化数组)
* 仍可被 tryFrom() 识别,不会被静默丢弃。
*/
final class SessionMessage implements \JsonSerializable
{
Expand Down Expand Up @@ -67,27 +72,57 @@ public function jsonSerialize(): mixed
/**
* 从 session 中读取的值尝试构造实例.
*
* 兼容两种情况
* 兼容以下情况
* - PHP 序列化(session.serialization = php):值已经是 SessionMessage 对象
* - JSON 序列化(session.serialization = json):值是带类型标识的数组
* - 旧契约:外部代码直接 flash 的 MessageBag 实例,或其序列化后的数组
* (形如 ['title' => ['x'], 'message' => ['y']])
*/
public static function tryFrom(mixed $value): ?self
{
if ($value instanceof self) {
return $value;
}

if (
is_array($value)
&& ($value[self::JSON_CLASS_KEY] ?? null) === self::JSON_CLASS_VALUE
) {
if ($value instanceof MessageBag) {
$value = $value->getMessages();
}

if (! is_array($value)) {
return null;
}

if (($value[self::JSON_CLASS_KEY] ?? null) === self::JSON_CLASS_VALUE) {
return new self(
is_string($value['title'] ?? null) ? $value['title'] : '',
is_string($value['message'] ?? null) ? $value['message'] : '',
is_array($value['options'] ?? null) ? $value['options'] : [],
);
}

return null;
return self::tryFromMessageBagArray($value);
}

/**
* 尝试按 MessageBag 序列化数组的形态构造实例(title/message 值为字符串数组).
*/
private static function tryFromMessageBagArray(array $value): ?self
{
if (
(! isset($value['title']) && ! isset($value['message']))
|| ! is_array($value['title'] ?? [])
|| ! is_array($value['message'] ?? [])
) {
return null;
}

$title = $value['title'][0] ?? '';
$message = $value['message'][0] ?? '';

if (! is_string($title) || ! is_string($message)) {
return null;
}

return new self($title, $message);
}
}
8 changes: 5 additions & 3 deletions src/Traits/HasModelOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ trait HasModelOptions
* Load options from current selected resource(s).
*
* @param string $model
* @param string $idField
* @param string|null $idField 默认取模型主键
* @param string $textField
* @return $this
*/
public function model($model, string $idField = 'id', string $textField = 'name')
public function model($model, ?string $idField = null, string $textField = 'name')
{
if (! class_exists($model)
|| ! in_array(Model::class, class_parents($model))
|| ! in_array(Model::class, class_parents($model), true)
) {
throw new RuntimeException("[$model] must be a valid model class");
}

$idField ??= (new $model())->getKeyName();

$this->options = function ($value) use ($model, $idField, $textField) {
if (empty($value)) {
return [];
Expand Down
2 changes: 1 addition & 1 deletion src/Widgets/Dropdown.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ protected function renderOption($k, $v)
$v = $builder->call($this, $v, $k);
}

$v = str_starts_with($v, '</a>') ? $v : "<a href='javascript:void(0)'>$v</a>";
$v = str_contains($v, '</a>') ? $v : "<a href='javascript:void(0)'>$v</a>";
$v = "<li class='dropdown-item'>$v</li>";

if ($this->divider) {
Expand Down
52 changes: 51 additions & 1 deletion tests/Unit/SessionMessageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,54 @@ public function test_try_from_ignores_non_array_options(): void
$this->assertSame('hello', $restored->getMessage());
$this->assertSame([], $restored->getOptions());
}
}

// -----------------------------------------------------------------------
// tryFrom — 旧契约 MessageBag 兼容
// -----------------------------------------------------------------------

public function test_try_from_accepts_legacy_message_bag_instance(): void
{
$bag = new \Illuminate\Support\MessageBag([
'title' => '操作成功',
'message' => '记录已保存',
]);

$restored = SessionMessage::tryFrom($bag);

$this->assertNotNull($restored);
$this->assertSame('操作成功', $restored->getTitle());
$this->assertSame('记录已保存', $restored->getMessage());
}

public function test_try_from_accepts_legacy_message_bag_serialized_array(): void
{
$bag = new \Illuminate\Support\MessageBag([
'title' => '操作成功',
'message' => '记录已保存',
]);
$array = json_decode(json_encode($bag), true);

$restored = SessionMessage::tryFrom($array);

$this->assertNotNull($restored);
$this->assertSame('操作成功', $restored->getTitle());
$this->assertSame('记录已保存', $restored->getMessage());
}

public function test_try_from_accepts_message_bag_with_only_title(): void
{
$restored = SessionMessage::tryFrom(['title' => ['仅标题']]);

$this->assertNotNull($restored);
$this->assertSame('仅标题', $restored->getTitle());
$this->assertSame('', $restored->getMessage());
}

public function test_try_from_rejects_message_bag_shaped_array_with_non_string_values(): void
{
$this->assertNull(SessionMessage::tryFrom([
'title' => [['nested']],
'message' => ['hi'],
]));
}
}
Loading