generated from dellevin/template
影视添加配音角色
This commit is contained in:
@@ -415,6 +415,7 @@ class _PersonDetailPageState extends State<PersonDetailPage> {
|
|||||||
'director' => '导演',
|
'director' => '导演',
|
||||||
'writer' => '编剧',
|
'writer' => '编剧',
|
||||||
'actor' => '演员',
|
'actor' => '演员',
|
||||||
|
'voiceActor' => '配音',
|
||||||
'author' => '作者',
|
'author' => '作者',
|
||||||
'translator' => '译者',
|
'translator' => '译者',
|
||||||
'developer' => '开发者',
|
'developer' => '开发者',
|
||||||
|
|||||||
@@ -421,6 +421,7 @@ class _PersonInfoSheetState extends State<PersonInfoSheet> {
|
|||||||
'director' => '导演',
|
'director' => '导演',
|
||||||
'writer' => '编剧',
|
'writer' => '编剧',
|
||||||
'actor' => '演员',
|
'actor' => '演员',
|
||||||
|
'voiceActor' => '配音',
|
||||||
'author' => '作者',
|
'author' => '作者',
|
||||||
'translator' => '译者',
|
'translator' => '译者',
|
||||||
'developer' => '开发者',
|
'developer' => '开发者',
|
||||||
|
|||||||
@@ -48,25 +48,29 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
|
|||||||
final person = people.where((p) => p.id == personId).firstOrNull;
|
final person = people.where((p) => p.id == personId).firstOrNull;
|
||||||
if (person == null) return;
|
if (person == null) return;
|
||||||
final existing = byPerson[personId];
|
final existing = byPerson[personId];
|
||||||
|
if (withCharacter) {
|
||||||
|
final c = r.characterName as String?;
|
||||||
|
if (c != null && c.isNotEmpty) {
|
||||||
|
existing?.characters.putIfAbsent(roleType, () => []).add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
existing.roleTypes.add(roleType);
|
existing.roleTypes.add(roleType);
|
||||||
if (withCharacter) {
|
return;
|
||||||
final c = r.characterName as String?;
|
|
||||||
if (c != null && c.isNotEmpty) existing.characterNames.add(c);
|
|
||||||
}
|
}
|
||||||
} else {
|
final characters = <String, List<String>>{};
|
||||||
final characterNames = <String>[];
|
|
||||||
if (withCharacter) {
|
if (withCharacter) {
|
||||||
final c = r.characterName as String?;
|
final c = r.characterName as String?;
|
||||||
if (c != null && c.isNotEmpty) characterNames.add(c);
|
if (c != null && c.isNotEmpty) {
|
||||||
|
characters[roleType] = [c];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
byPerson[personId] = _PersonRole(
|
byPerson[personId] = _PersonRole(
|
||||||
person: person,
|
person: person,
|
||||||
roleTypes: [roleType],
|
roleTypes: [roleType],
|
||||||
characterNames: characterNames,
|
characters: characters,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
switch (widget.workType) {
|
switch (widget.workType) {
|
||||||
case 'movie':
|
case 'movie':
|
||||||
@@ -109,6 +113,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
|
|||||||
'director' => '导演',
|
'director' => '导演',
|
||||||
'writer' => '编剧',
|
'writer' => '编剧',
|
||||||
'actor' => '演员',
|
'actor' => '演员',
|
||||||
|
'voiceActor' => '配音',
|
||||||
'author' => '作者',
|
'author' => '作者',
|
||||||
'translator' => '译者',
|
'translator' => '译者',
|
||||||
'developer' => '开发者',
|
'developer' => '开发者',
|
||||||
@@ -116,7 +121,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 角色排序权重:演员放最后
|
/// 角色排序权重:演员/配音放最后
|
||||||
int _roleWeight(String roleType) {
|
int _roleWeight(String roleType) {
|
||||||
return switch (roleType) {
|
return switch (roleType) {
|
||||||
'director' => 0,
|
'director' => 0,
|
||||||
@@ -125,6 +130,7 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
|
|||||||
'translator' => 3,
|
'translator' => 3,
|
||||||
'developer' => 4,
|
'developer' => 4,
|
||||||
'actor' => 99,
|
'actor' => 99,
|
||||||
|
'voiceActor' => 99,
|
||||||
_ => 50,
|
_ => 50,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -134,19 +140,22 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
|
|||||||
/// 「导演 / 演员 饰 唐僧 / 演员 饰 孙悟空」
|
/// 「导演 / 演员 饰 唐僧 / 演员 饰 孙悟空」
|
||||||
String _buildRoleText(_PersonRole item) {
|
String _buildRoleText(_PersonRole item) {
|
||||||
final parts = <String>[];
|
final parts = <String>[];
|
||||||
// 非演员角色:去重后按权重排序
|
final perfRoleTypes = ['actor', 'voiceActor'];
|
||||||
final nonActor = item.roleTypes.where((r) => r != 'actor').toSet().toList()
|
// 非演员/配音角色:去重后按权重排序
|
||||||
|
final nonPerf = item.roleTypes.where((r) => !perfRoleTypes.contains(r)).toSet().toList()
|
||||||
..sort((a, b) => _roleWeight(a).compareTo(_roleWeight(b)));
|
..sort((a, b) => _roleWeight(a).compareTo(_roleWeight(b)));
|
||||||
parts.addAll(nonActor.map(_roleLabel));
|
parts.addAll(nonPerf.map(_roleLabel));
|
||||||
|
|
||||||
// 演员角色:每个饰演角色名单独成段
|
// 演员/配音角色:每个饰演角色名单独成段
|
||||||
final isActor = item.roleTypes.contains('actor');
|
for (final roleType in perfRoleTypes) {
|
||||||
if (isActor) {
|
if (!item.roleTypes.contains(roleType)) continue;
|
||||||
if (item.characterNames.isEmpty) {
|
final label = _roleLabel(roleType);
|
||||||
parts.add('演员');
|
final names = item.characters[roleType] ?? const [];
|
||||||
|
if (names.isEmpty) {
|
||||||
|
parts.add(label);
|
||||||
} else {
|
} else {
|
||||||
for (final c in item.characterNames) {
|
for (final c in names) {
|
||||||
parts.add('演员 饰 $c');
|
parts.add('$label 饰 $c');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,11 +291,11 @@ class _WorkPeopleSectionState extends State<WorkPeopleSection> {
|
|||||||
class _PersonRole {
|
class _PersonRole {
|
||||||
final Person person;
|
final Person person;
|
||||||
final List<String> roleTypes;
|
final List<String> roleTypes;
|
||||||
final List<String> characterNames;
|
final Map<String, List<String>> characters; // roleType → 角色名列表(actor/voiceActor)
|
||||||
|
|
||||||
_PersonRole({
|
_PersonRole({
|
||||||
required this.person,
|
required this.person,
|
||||||
required this.roleTypes,
|
required this.roleTypes,
|
||||||
required this.characterNames,
|
required this.characters,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class _WorkSelectorPageState extends State<WorkSelectorPage> {
|
|||||||
List<Person> _matchedPeople = [];
|
List<Person> _matchedPeople = [];
|
||||||
bool _searching = false;
|
bool _searching = false;
|
||||||
|
|
||||||
static const _movieRoles = [('director', '导演'), ('writer', '编剧'), ('actor', '演员')];
|
static const _movieRoles = [('director', '导演'), ('writer', '编剧'), ('actor', '演员'), ('voiceActor', '配音')];
|
||||||
static const _bookRoles = [('author', '作者'), ('translator', '译者')];
|
static const _bookRoles = [('author', '作者'), ('translator', '译者')];
|
||||||
static const _gameRoles = [('developer', '开发者')];
|
static const _gameRoles = [('developer', '开发者')];
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ class _WorkSelectorPageState extends State<WorkSelectorPage> {
|
|||||||
workId: entry.workId,
|
workId: entry.workId,
|
||||||
workType: entry.workType,
|
workType: entry.workType,
|
||||||
roleType: roleType,
|
roleType: roleType,
|
||||||
characterName: entry.workType == 'movie' && roleType == 'actor' ? entry.characterName : null,
|
characterName: entry.workType == 'movie' && (roleType == 'actor' || roleType == 'voiceActor') ? entry.characterName : null,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -556,6 +556,9 @@ class _WorkSelectorPageState extends State<WorkSelectorPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showCharacterNameDialog(_WorkRoleEntry entry, String? current) {
|
void _showCharacterNameDialog(_WorkRoleEntry entry, String? current) {
|
||||||
|
final isVoiceActor = entry.roleType == 'voiceActor';
|
||||||
|
final title = isVoiceActor ? '配音角色' : '饰演角色';
|
||||||
|
final hint = isVoiceActor ? '如:米老鼠' : '如:关羽';
|
||||||
final ctrl = TextEditingController(text: current ?? '');
|
final ctrl = TextEditingController(text: current ?? '');
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -564,14 +567,14 @@ class _WorkSelectorPageState extends State<WorkSelectorPage> {
|
|||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
title: Text('饰演角色', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
title: Text(title, style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
||||||
content: TextField(
|
content: TextField(
|
||||||
controller: ctrl,
|
controller: ctrl,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
style: TextStyle(fontSize: 15, color: colors.onSurface),
|
style: TextStyle(fontSize: 15, color: colors.onSurface),
|
||||||
cursorColor: colors.primary,
|
cursorColor: colors.primary,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: '如:关羽',
|
hintText: hint,
|
||||||
hintStyle: TextStyle(color: colors.onSurface.withValues(alpha: 0.3)),
|
hintStyle: TextStyle(color: colors.onSurface.withValues(alpha: 0.3)),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: colors.surfaceContainerHigh,
|
fillColor: colors.surfaceContainerHigh,
|
||||||
@@ -699,8 +702,8 @@ class _WorkSelectorPageState extends State<WorkSelectorPage> {
|
|||||||
// 职业标签/下拉
|
// 职业标签/下拉
|
||||||
_buildRoleDropdown(entry, colors),
|
_buildRoleDropdown(entry, colors),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
// 角色名(仅影视演员)
|
// 角色名(影视演员/配音)
|
||||||
if (entry.workType == 'movie' && entry.roleType == 'actor') ...[
|
if (entry.workType == 'movie' && (entry.roleType == 'actor' || entry.roleType == 'voiceActor')) ...[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => _showCharacterNameDialog(entry, entry.characterName),
|
onTap: () => _showCharacterNameDialog(entry, entry.characterName),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|||||||
Reference in New Issue
Block a user