Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
I
img-manager
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ivan Lan
img-manager
Commits
5565aa5b
Commit
5565aa5b
authored
Mar 10, 2025
by
Ivan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 通过时选择标签
parent
a152fff4
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
184 additions
and
155 deletions
+184
-155
images_controller.rb
app/controllers/admin/images_controller.rb
+8
-54
images_controller.rb
app/controllers/images_controller.rb
+14
-9
tags_controller.rb
app/controllers/tags_controller.rb
+51
-43
TagSearchInput.jsx
app/frontend/components/TagSearchInput.jsx
+22
-37
ComImageShow.jsx
app/frontend/components/images/ComImageShow.jsx
+86
-9
Edit.jsx
app/frontend/pages/images/Edit.jsx
+3
-3
No files found.
app/controllers/admin/images_controller.rb
View file @
5565aa5b
...
...
@@ -13,7 +13,6 @@ class Admin::ImagesController < ApplicationController
tag:
@tag
.
as_json
(
only:
[
:id
,
:name
,
:catalog
],
methods:
[
:images_count
]),
images:
@images
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
]),
filters:
params
[
:q
]
||
{},
tags:
Tag
.
all
.
to_json
,
pagination:
{
current_page:
@images
.
current_page
,
total_pages:
@images
.
total_pages
,
...
...
@@ -33,7 +32,6 @@ class Admin::ImagesController < ApplicationController
total_pages:
@images
.
total_pages
,
total_count:
@images
.
total_count
},
tags:
Tag
.
all
.
to_json
,
pending_count:
Image
.
pending
.
count
,
approved_count:
Image
.
approved
.
count
,
rejected_count:
Image
.
rejected
.
count
...
...
@@ -43,28 +41,23 @@ class Admin::ImagesController < ApplicationController
def
show
render
inertia:
"admin/images/Show"
,
props:
{
image:
@image
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
]),
tags:
Tag
.
all
.
as_json
image:
@image
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
])
}
end
def
edit
render
inertia:
"admin/images/Edit"
,
props:
{
image:
@image
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
]),
tags:
Tag
.
all
.
as_json
image:
@image
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
])
}
end
def
new
if
params
[
:tag_id
].
present?
render
inertia:
"admin/tags/images/New"
,
props:
{
tag:
@tag
.
as_json
(
only:
[
:id
,
:name
,
:catalog
],
methods:
[
:images_count
]),
tags:
Tag
.
all
.
as_json
tag:
@tag
.
as_json
(
only:
[
:id
,
:name
,
:catalog
],
methods:
[
:images_count
])
}
else
render
inertia:
"admin/images/New"
,
props:
{
tags:
Tag
.
all
.
as_json
}
render
inertia:
"admin/images/New"
,
props:
{}
end
end
...
...
@@ -112,7 +105,6 @@ class Admin::ImagesController < ApplicationController
else
render
inertia:
"admin/images/Edit"
,
props:
{
image:
@image
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
,
:errors
]),
tags:
Tag
.
all
.
as_json
,
errors:
@image
.
errors
},
status: :unprocessable_entity
end
...
...
@@ -123,50 +115,12 @@ class Admin::ImagesController < ApplicationController
redirect_to
admin_images_path
,
notice:
"Image was successfully deleted."
end
def
pending
@q
=
Image
.
pending
.
includes
(
:user
,
:tags
).
ransack
(
params
[
:q
])
@images
=
@q
.
result
(
distinct:
true
).
with_attached_file
.
order
(
created_at: :desc
).
page
(
params
[
:page
]).
per
(
20
)
render
inertia:
"admin/images/Pending"
,
props:
{
images:
@images
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
]),
filters:
params
[
:q
]
||
{},
total_count:
Image
.
count
,
pending_count:
Image
.
pending
.
count
,
approved_count:
Image
.
approved
.
count
,
rejected_count:
Image
.
rejected
.
count
}
end
def
approved
@q
=
Image
.
approved
.
includes
(
:user
,
:tags
).
ransack
(
params
[
:q
])
@images
=
@q
.
result
(
distinct:
true
).
with_attached_file
.
order
(
created_at: :desc
).
page
(
params
[
:page
]).
per
(
20
)
render
inertia:
"admin/images/Approved"
,
props:
{
images:
@images
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
]),
filters:
params
[
:q
]
||
{},
total_count:
Image
.
count
,
pending_count:
Image
.
pending
.
count
,
approved_count:
Image
.
approved
.
count
,
rejected_count:
Image
.
rejected
.
count
}
end
def
rejected
@q
=
Image
.
rejected
.
includes
(
:user
,
:tags
).
ransack
(
params
[
:q
])
@images
=
@q
.
result
(
distinct:
true
).
with_attached_file
.
order
(
created_at: :desc
).
page
(
params
[
:page
]).
per
(
20
)
render
inertia:
"admin/images/Rejected"
,
props:
{
images:
@images
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
]),
filters:
params
[
:q
]
||
{},
total_count:
Image
.
count
,
pending_count:
Image
.
pending
.
count
,
approved_count:
Image
.
approved
.
count
,
rejected_count:
Image
.
rejected
.
count
}
end
def
approve
Image
.
transaction
do
@image
.
approved!
@image
.
set_tags_by_ids
(
params
[
:tag_ids
])
if
params
[
:tag_ids
].
present?
end
redirect_to
admin_image_path
(
@image
),
notice:
"Image was successfully approved."
end
...
...
app/controllers/images_controller.rb
View file @
5565aa5b
...
...
@@ -15,7 +15,6 @@ class ImagesController < ApplicationController
tag:
@tag
.
as_json
(
only:
[
:id
,
:name
,
:catalog
],
methods:
[
:images_count
]),
images:
@images
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
]),
filters:
params
[
:q
]
||
{},
tags:
Tag
.
all
.
to_json
,
pagination:
{
current_page:
@images
.
current_page
,
total_pages:
@images
.
total_pages
,
...
...
@@ -30,7 +29,6 @@ class ImagesController < ApplicationController
render
inertia:
"images/Index"
,
props:
{
images:
@images
.
as_json
(
include:
[
:user
,
:tags
],
methods:
[
:file_url
,
:thumbnail_url
,
:medium_url
]),
filters:
params
[
:q
]
||
{},
tags:
Tag
.
all
.
to_json
,
pagination:
{
current_page:
@images
.
current_page
,
total_pages:
@images
.
total_pages
,
...
...
@@ -51,9 +49,7 @@ class ImagesController < ApplicationController
def
new
@image
=
Image
.
new
render
inertia:
"images/New"
,
props:
{
tags:
Tag
.
all
.
as_json
}
render
inertia:
"images/New"
,
props:
{}
end
def
create
...
...
@@ -75,13 +71,13 @@ class ImagesController < ApplicationController
def
edit
authorize_user
unless
@image
.
status
==
"pending"
||
Current
.
user
&
.
admin?
return
redirect_to
image_path
(
@image
),
notice:
"没有权限修改"
end
render
inertia:
"images/Edit"
,
props:
{
image:
@image
.
as_json
(
include:
[
:tags
],
methods:
[
:file_url
]),
tags:
Tag
.
all
.
as_json
image:
@image
.
as_json
(
include:
[
:tags
],
methods:
[
:file_url
])
}
end
...
...
@@ -101,23 +97,32 @@ class ImagesController < ApplicationController
else
render
inertia:
"images/Edit"
,
props:
{
image:
@image
.
as_json
(
include:
[
:tags
],
methods:
[
:file_url
,
:errors
]),
tags:
Tag
.
all
.
as_json
,
errors:
@image
.
errors
},
status: :unprocessable_entity
end
end
def
destroy
@image
.
destroy
authorize_admin
@image
.
destroy!
redirect_to
images_path
,
notice:
"Image was successfully deleted."
end
def
approve
authorize_admin
Image
.
transaction
do
@image
.
approved!
@image
.
set_tags_by_ids
(
params
[
:tag_ids
])
if
params
[
:tag_ids
].
present?
end
redirect_to
image_path
(
@image
),
notice:
"Image was successfully approved."
end
def
reject
authorize_admin
@image
.
rejected!
redirect_to
image_path
(
@image
),
notice:
"Image was rejected."
end
...
...
app/controllers/tags_controller.rb
View file @
5565aa5b
class
TagsController
<
ApplicationController
allow_unauthenticated_access
only:
[
:index
]
#
allow_unauthenticated_access only: [ :index ]
def
index
@tags
=
Tag
.
includes
(
:images
).
all
...
...
@@ -17,59 +17,67 @@ class TagsController < ApplicationController
@grouped_tags
[
catalog
]
=
grouped_tags
[
catalog
]
end
render
inertia:
"tags/Index"
,
props:
{
tags:
@grouped_tags
.
transform_values
do
|
tags
|
data
=
@grouped_tags
.
transform_values
do
|
tags
|
tags
.
map
do
|
tag
|
tag
.
as_json
(
only:
[
:id
,
:name
,
:catalog
],
methods:
[
:images_count
])
end
end
}
end
def
new
render
inertia:
"tags/New"
end
def
create
@tag
=
Tag
.
new
(
tag_params
)
if
@tag
.
save
redirect_to
tags_path
,
notice:
"Tag was successfully created."
else
render
inertia:
"tags/New"
,
props:
{
tag:
@tag
.
as_json
,
errors:
@tag
.
errors
},
status: :unprocessable_entity
end
end
def
edit
@tag
=
Tag
.
find
(
params
[
:id
])
render
inertia:
"tags/Edit"
,
props:
{
tag:
@tag
.
as_json
(
only:
[
:id
,
:name
,
:catalog
])
respond_to
do
|
format
|
format
.
html
do
render
inertia:
"tags/Index"
,
props:
{
tags:
data
}
end
def
update
@tag
=
Tag
.
find
(
params
[
:id
])
if
@tag
.
update
(
tag_params
)
redirect_to
tags_path
,
notice:
"Tag was successfully updated."
else
render
inertia:
"tags/Edit"
,
props:
{
tag:
@tag
.
as_json
,
errors:
@tag
.
errors
},
status: :unprocessable_entity
format
.
json
{
render
json:
{
tags:
data
}
}
end
end
def
destroy
@tag
=
Tag
.
find
(
params
[
:id
])
@tag
.
destroy
redirect_to
tags_path
,
notice:
"Tag was successfully deleted."
end
# def new
# render inertia: "tags/New"
# end
# def create
# @tag = Tag.new(tag_params)
# if @tag.save
# redirect_to tags_path, notice: "Tag was successfully created."
# else
# render inertia: "tags/New", props: {
# tag: @tag.as_json,
# errors: @tag.errors
# }, status: :unprocessable_entity
# end
# end
# def edit
# @tag = Tag.find(params[:id])
# render inertia: "tags/Edit", props: {
# tag: @tag.as_json(only: [ :id, :name, :catalog ])
# }
# end
# def update
# @tag = Tag.find(params[:id])
# if @tag.update(tag_params)
# redirect_to tags_path, notice: "Tag was successfully updated."
# else
# render inertia: "tags/Edit", props: {
# tag: @tag.as_json,
# errors: @tag.errors
# }, status: :unprocessable_entity
# end
# end
# def destroy
# @tag = Tag.find(params[:id])
# @tag.destroy
# redirect_to tags_path, notice: "Tag was successfully deleted."
# end
private
...
...
app/frontend/components/TagSearchInput.jsx
View file @
5565aa5b
import
{
useState
,
useEffect
}
from
'react'
import
*
as
Popover
from
'@radix-ui/react-popover'
import
{
MagnifyingGlassIcon
,
XMarkIcon
}
from
'@heroicons/react/24/outline'
import
{
isEqual
}
from
'lodash-e
s'
import
axios
from
'axio
s'
export
default
function
TagSearchInput
({
value
,
onChange
,
allTags
=
{},
placeholder
=
"输入标签名称搜索"
})
{
export
default
function
TagSearchInput
({
value
,
onChange
,
placeholder
=
"输入标签名称搜索"
})
{
// value 现在是逗号分隔的标签 ID 列表
const
[
searchQuery
,
setSearchQuery
]
=
useState
(
''
)
const
[
selectedTags
,
setSelectedTags
]
=
useState
([])
...
...
@@ -12,49 +12,34 @@ export default function TagSearchInput({ value, onChange, allTags = {}, placehol
const
[
filteredTags
,
setFilteredTags
]
=
useState
({})
const
[
internalValue
,
setInternalValue
]
=
useState
(
value
||
[])
//
处理标签数据和选中标签
//
获取标签数据
useEffect
(()
=>
{
// 处理标签数据,支持数组或已分组的对象格式
let
processedTags
=
{}
let
tagsData
=
allTags
// 如果是 JSON 字符串,先解析
if
(
typeof
allTags
===
'string'
)
{
const
fetchTags
=
async
()
=>
{
try
{
tagsData
=
JSON
.
parse
(
allTags
)
}
catch
(
e
)
{
console
.
error
(
'Failed to parse tags JSON:'
,
e
)
tagsData
=
[]
}
}
const
response
=
await
axios
.
get
(
'/tags.json'
)
// 如果传入的是数组,需要按catalog属性分组
if
(
Array
.
isArray
(
tagsData
))
{
// 按catalog属性分组标签
const
groupedByCategory
=
{}
tagsData
.
forEach
(
tag
=>
{
const
catalog
=
tag
.
catalog
||
''
if
(
!
groupedByCategory
[
catalog
])
{
groupedByCategory
[
catalog
]
=
[]
// 处理获取到的标签数据
if
(
response
.
data
&&
response
.
data
.
tags
)
{
setAvailableTags
(
response
.
data
.
tags
)
}
groupedByCategory
[
catalog
].
push
(
tag
)
})
processedTags
=
groupedByCategory
}
else
{
// 如果已经是分组好的对象,直接使用
processedTags
=
tagsData
||
{}
setSelectedTags
(
Object
.
values
(
response
.
data
.
tags
).
flat
().
filter
(
tag
=>
internalValue
.
includes
(
tag
.
id
)))
}
catch
(
error
)
{
console
.
error
(
'Error fetching tags:'
,
error
)
setAvailableTags
({})
}
}
// 更新分组后的标签
setAvailableTags
(
processedTags
)
fetchTags
()
},
[]
)
// 处理选中的标签
// 使用 JSON.stringify 比较数组,因于 isEqual 可能不总是可靠的用于空数组
useEffect
(()
=>
{
// 使用 JSON.stringify 比较数组
const
valueChanged
=
JSON
.
stringify
(
value
)
!==
JSON
.
stringify
(
internalValue
);
// 如果值发生变化
或强制更新
,则更新内部值
// 如果值发生变化,则更新内部值
if
(
valueChanged
)
{
const
newValue
=
value
||
[];
setInternalValue
(
newValue
);
...
...
@@ -65,7 +50,7 @@ export default function TagSearchInput({ value, onChange, allTags = {}, placehol
// 从所有可用标签中找出已选标签
const
selected
=
[];
Object
.
values
(
processed
Tags
).
forEach
(
tagGroup
=>
{
Object
.
values
(
available
Tags
).
forEach
(
tagGroup
=>
{
tagGroup
.
forEach
(
tag
=>
{
if
(
tagIds
.
includes
(
tag
.
id
))
{
selected
.
push
(
tag
);
...
...
@@ -79,7 +64,7 @@ export default function TagSearchInput({ value, onChange, allTags = {}, placehol
setSelectedTags
([]);
}
}
},
[
allTags
,
value
,
internalValue
])
},
[
value
,
internalValue
,
availableTags
])
// 根据搜索词过滤标签
useEffect
(()
=>
{
...
...
@@ -174,7 +159,7 @@ export default function TagSearchInput({ value, onChange, allTags = {}, placehol
</
div
>
<
Popover
.
Portal
>
<
Popover
.
Content
className=
"bg-white rounded-md shadow-lg p-4 w-72 max-w-[calc(100vw-2rem)] z-50"
className=
"bg-white rounded-md shadow-lg p-4 w-72 max-w-[calc(100vw-2rem)] z-
20
50"
sideOffset=
{
5
}
>
<
div
className=
"space-y-4"
>
...
...
app/frontend/components/images/ComImageShow.jsx
View file @
5565aa5b
import
{
useState
}
from
'react'
import
{
Link
}
from
'@inertiajs/react'
import
{
useState
,
useEffect
}
from
'react'
import
{
Link
,
router
}
from
'@inertiajs/react'
import
*
as
Dialog
from
'@radix-ui/react-dialog'
import
*
as
Form
from
'@radix-ui/react-form'
import
ComImageStatusTag
from
'./ComImageStatusTag'
import
TagSearchInput
from
'../TagSearchInput'
export
default
function
ComImageShow
({
path
,
image
,
can_edit
,
can_approve
,
isAdmin
=
false
})
{
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
false
)
const
[
isApproveDialogOpen
,
setIsApproveDialogOpen
]
=
useState
(
false
)
const
[
selectedTagIds
,
setSelectedTagIds
]
=
useState
(
image
.
tags
?.
map
(
tag
=>
tag
.
id
)
||
[])
const
[
isSubmitting
,
setIsSubmitting
]
=
useState
(
false
)
// Update selectedTagIds when image changes
useEffect
(()
=>
{
if
(
image
&&
image
.
tags
)
{
setSelectedTagIds
(
image
.
tags
.
map
(
tag
=>
tag
.
id
));
}
},
[
image
])
const
handleApprove
=
()
=>
{
setIsSubmitting
(
true
)
router
.
patch
(
`
${
path
}
/approve`
,
{
tag_ids
:
selectedTagIds
},
{
onSuccess
:
()
=>
{
setIsApproveDialogOpen
(
false
)
setIsSubmitting
(
false
)
},
onError
:
()
=>
{
setIsSubmitting
(
false
)
}
})
}
return
(
<
div
className=
"w-full bg-white shadow overflow-hidden sm:rounded-lg"
>
...
...
@@ -27,14 +54,13 @@ export default function ComImageShow({ path, image, can_edit, can_approve, isAdm
)
}
{
can_approve
&&
image
.
status
===
'pending'
&&
(
<>
<
Link
href=
{
`${path}/approve`
}
method=
"patch"
as=
"button"
<
button
type=
"button"
onClick=
{
()
=>
setIsApproveDialogOpen
(
true
)
}
className=
"inline-flex items-center px-3 py-1.5 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
>
通过
</
Link
>
</
button
>
<
Link
href=
{
`${path}/reject`
}
method=
"patch"
...
...
@@ -122,8 +148,8 @@ export default function ComImageShow({ path, image, can_edit, can_approve, isAdm
{
/* 全屏图片模态框 */
}
<
Dialog
.
Root
open=
{
isModalOpen
}
onOpenChange=
{
setIsModalOpen
}
>
<
Dialog
.
Portal
>
<
Dialog
.
Overlay
className=
"fixed inset-0 bg-black/50"
/>
<
Dialog
.
Content
className=
"fixed inset-0 flex items-center justify-center z-50"
>
<
Dialog
.
Overlay
className=
"fixed inset-0 bg-black/50
z-100
"
/>
<
Dialog
.
Content
className=
"fixed inset-0 flex items-center justify-center z-
1
50"
>
<
Dialog
.
Title
className=
"hidden"
/>
<
div
className=
"relative w-full h-full max-w-screen-xl max-h-screen p-4"
>
<
img
...
...
@@ -156,6 +182,57 @@ export default function ComImageShow({ path, image, can_edit, can_approve, isAdm
</
Dialog
.
Content
>
</
Dialog
.
Portal
>
</
Dialog
.
Root
>
{
/* 审核通过确认对话框 */
}
<
Dialog
.
Root
open=
{
isApproveDialogOpen
}
onOpenChange=
{
setIsApproveDialogOpen
}
>
<
Dialog
.
Portal
>
<
Dialog
.
Overlay
className=
"fixed inset-0 bg-black/50 z-100"
/>
<
Dialog
.
Content
className=
"fixed z-150 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-md bg-white rounded-lg shadow-xl p-6 z-50"
>
<
Dialog
.
Title
className=
"text-lg font-medium text-gray-900 mb-4"
>
确认通过审核
</
Dialog
.
Title
>
<
div
className=
"mb-4"
>
<
p
className=
"text-sm text-gray-500 mb-4"
>
请为此图片选择合适的标签,然后点击确认通过审核。
</
p
>
<
Form
.
Root
>
<
Form
.
Field
name=
"tags"
className=
"space-y-2"
>
<
Form
.
Label
className=
"block text-sm font-medium text-gray-700"
>
标签
</
Form
.
Label
>
<
Form
.
Control
asChild
>
<
TagSearchInput
value=
{
selectedTagIds
}
onChange=
{
(
value
)
=>
setSelectedTagIds
(
value
)
}
placeholder=
"点击搜索图标添加标签"
/>
</
Form
.
Control
>
</
Form
.
Field
>
</
Form
.
Root
>
</
div
>
<
div
className=
"flex justify-end space-x-3 mt-6"
>
<
Dialog
.
Close
asChild
>
<
button
type=
"button"
className=
"px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
取消
</
button
>
</
Dialog
.
Close
>
<
button
type=
"button"
disabled=
{
isSubmitting
}
onClick=
{
handleApprove
}
className=
"inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-green-600 border border-transparent rounded-md shadow-sm hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50"
>
{
isSubmitting
?
'处理中...'
:
'确认通过'
}
</
button
>
</
div
>
</
Dialog
.
Content
>
</
Dialog
.
Portal
>
</
Dialog
.
Root
>
</
div
>
)
}
app/frontend/pages/images/Edit.jsx
View file @
5565aa5b
import
{
Head
,
useForm
}
from
'@inertiajs/react'
import
{
Head
,
useForm
,
router
}
from
'@inertiajs/react'
import
Layout
from
'../Layout'
import
ImageUploadForm
from
'../../components/ImageUploadForm'
...
...
@@ -8,14 +8,14 @@ export default function Edit({ image, tags, auth, errors = {} }) {
?
image
.
tags
.
map
(
tag
=>
tag
.
id
)
:
[]
const
{
data
,
setData
,
p
atch
,
p
rocessing
}
=
useForm
({
const
{
data
,
setData
,
processing
}
=
useForm
({
title
:
image
.
title
||
''
,
tag_ids
:
initialTagIds
,
// 使用tag_ids而不是tags
})
const
handleSubmit
=
(
e
)
=>
{
e
.
preventDefault
()
patch
(
`/images/
${
image
.
id
}
`
)
router
.
patch
(
`/admin/images/
${
image
.
id
}
`
,
{
image
:
data
}
)
}
return
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment