am.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <template>
  2. <Tabs v-model:activeKey="activeKey" width="100%">
  3. <TabPane key="setting" tab="设置">
  4. <ui-group borderBottom>
  5. <ui-group-option>
  6. <SignItem label="名称" not-apply>
  7. <ui-input
  8. width="100%"
  9. type="text"
  10. ref="nameInput"
  11. class="nameInput"
  12. placeholder="请输入名称"
  13. v-model="am.title"
  14. :maxlength="100"
  15. />
  16. </SignItem>
  17. </ui-group-option>
  18. <ui-group-option class="item">
  19. <span class="label">显示名称</span>
  20. <span class="oper"> <Switch v-model:checked="am.showTitle" /> </span>
  21. </ui-group-option>
  22. </ui-group>
  23. <ui-group borderBottom>
  24. <ui-group-option>
  25. <SignItem label="文字大小" @apply-global="$emit('applyGlobal', 'fontSize')">
  26. <Slider v-model:value="am.fontSize" :min="12" :max="60" :step="0.1" />
  27. </SignItem>
  28. </ui-group-option>
  29. </ui-group>
  30. <ui-group borderBottom>
  31. <ui-group-option>
  32. <SignItem
  33. label="可见范围"
  34. v-if="!am.globalVisibility"
  35. @apply-global="$emit('applyGlobal', 'visibilityRange')"
  36. >
  37. <Slider v-model:value="am.visibilityRange" :min="1" :max="1000" :step="0.1" />
  38. </SignItem>
  39. </ui-group-option>
  40. <ui-group-option>
  41. <ui-input
  42. type="checkbox"
  43. label="全部范围可视"
  44. :modelValue="!!am.globalVisibility"
  45. @update:modelValue="(v: boolean) => am.globalVisibility = v"
  46. />
  47. </ui-group-option>
  48. </ui-group>
  49. </TabPane>
  50. <TabPane key="animation" tab="动画">
  51. <ui-group borderBottom>
  52. <ui-group-option class="item">
  53. <span class="label">加帧</span>
  54. <span class="oper" @click="$emit('addFrame')">
  55. <ui-icon type="keys_a" ctrl />
  56. </span>
  57. </ui-group-option>
  58. <ui-group-option class="item">
  59. <span class="label">路径</span>
  60. <span class="oper">
  61. <ui-icon @click="visibleSelectPath = true" type="add_a" ctrl />
  62. </span>
  63. </ui-group-option>
  64. <ui-group-option class="item">
  65. <span class="label">字幕</span>
  66. <span class="oper">
  67. <ui-icon
  68. @click="$emit('addSubtitle', { background: '#000' })"
  69. type="add_a"
  70. ctrl
  71. />
  72. </span>
  73. </ui-group-option>
  74. </ui-group>
  75. <ui-group borderBottom v-if="amActions.length">
  76. <ui-group-option class="item">
  77. <span class="label">动作库</span>
  78. </ui-group-option>
  79. <ui-group-option class="item action-item" v-for="action in amActions">
  80. <span class="label">{{ action.title }}</span>
  81. <span class="oper">
  82. <ui-icon
  83. type="add_a"
  84. ctrl
  85. @click="
  86. $emit('addAction', {
  87. amplitude: 1,
  88. speed: 1,
  89. key: action.action,
  90. name: action.title,
  91. duration: getActionDur(action.action),
  92. })
  93. "
  94. />
  95. </span>
  96. </ui-group-option>
  97. <!-- <ui-group-option class="actions">
  98. <div
  99. v-for="action in amActions"
  100. @click="$emit('addAction', { key: action.action, name: action.title })"
  101. >
  102. <img :src="action.url" />
  103. <span>{{ action.title }}</span>
  104. </div>
  105. </ui-group-option> -->
  106. </ui-group>
  107. </TabPane>
  108. </Tabs>
  109. <Modal
  110. width="400px"
  111. title="选择路径"
  112. :open="visibleSelectPath"
  113. @ok="selectPathHandler"
  114. @cancel="visibleSelectPath = false"
  115. okText="确定"
  116. cancelText="取消"
  117. class="model-table"
  118. >
  119. <div style="margin: 20px 0">
  120. <ui-input
  121. width="100%"
  122. type="select"
  123. :options="options"
  124. placeholder="请选择路径"
  125. v-model="pathId"
  126. />
  127. </div>
  128. </Modal>
  129. </template>
  130. <script lang="ts" setup>
  131. import { Switch, Slider, TabPane, Tabs } from "ant-design-vue";
  132. import { AnimationModel } from "@/api";
  133. import SignItem from "@/views/tagging-position/sign-item.vue";
  134. import { computed, ref } from "vue";
  135. import { round } from "@/utils";
  136. import { paths } from "@/store/path";
  137. import Message from "bill/components/message/message.vue";
  138. import { Modal } from "ant-design-vue";
  139. import { amMap, getAMKey } from "@/sdk/association/animation";
  140. const props = defineProps<{ am: AnimationModel }>();
  141. const emit = defineEmits<{
  142. (e: "addFrame" | "addPath" | "addSubtitle" | "addAction", preset?: any): void;
  143. (e: "applyGlobal", d: keyof AnimationModel): void;
  144. }>();
  145. const activeKey = ref("setting");
  146. const actionsMap: Record<string, string> = {
  147. sit_to_stand: "坐:站起",
  148. fist_pump: "坐下",
  149. end_bicycle_sit_up: "躺:起来",
  150. hit_on_legs: "向后倒下",
  151. crawling: "爬",
  152. medium_hit_to_head: "挨打",
  153. illegal_knee: "左膝盖",
  154. death_from_back_headshot: "向前倒地",
  155. dying: "向前倒地死掉",
  156. standard_walk: "标准走",
  157. start_walking: "起步走",
  158. left_turn_wbriefcase: "向左转",
  159. running: "标准跑",
  160. drunk_walk: "醉汉走",
  161. mma_kick: "右前踢",
  162. standing_jump: "标准向上跳",
  163. sitting: "标准坐",
  164. a_idel: "站立",
  165. peone_forward: "匍匐前行",
  166. wall_crash: "松手摔倒",
  167. head_hit: "头被击中",
  168. Fireman_Bash: "砸门",
  169. Fireman_Climb: "翻越",
  170. Fireman_Crouching: "蹲下",
  171. Women_Bash: "砸门",
  172. Women_Climb: "翻越",
  173. Women_Crouching: "蹲下",
  174. Women_Kicking: "踢门",
  175. "Women_Opening-Door-Inwards": "开门",
  176. "Fireman_Death-Form-Back-Headshot": "向前倒地",
  177. "Fireman_Illeagal-Knee-Left": "左膝盖",
  178. "Fireman_Illeagal-Knee-Right": "右膝盖",
  179. "Fireman_Opening-Door-Inwards": "开门",
  180. Fireman_Kicking: "踢门",
  181. "Fireman_Left-Turn-Wbirefcase": "左转弯",
  182. };
  183. Object.keys(actionsMap).forEach((action) => {
  184. const val = actionsMap[action];
  185. let key = action.toLowerCase().replaceAll(/( |-)/gi, "_");
  186. actionsMap[key] = val;
  187. });
  188. const keys = Object.keys(actionsMap);
  189. const amActions = computed(() => {
  190. const actions = amMap[getAMKey(props.am)]?.am?.getSupportActions() || [];
  191. return actions.map(({ name: action, duration }) => {
  192. let key = action.toLowerCase().replaceAll(/( |-)/gi, "_");
  193. !keys.find((k) => key.includes(k)) && console.log(key);
  194. key = keys.find((k) => key.includes(k)) || key;
  195. return { action, title: actionsMap[key] || action, duration };
  196. });
  197. });
  198. const options = computed(() =>
  199. paths.value.map((item) => ({ label: item.name, value: item.id }))
  200. );
  201. const pathId = ref<string>();
  202. const visibleSelectPath = ref(false);
  203. const selectPathHandler = () => {
  204. if (!pathId.value) {
  205. Message.error("请选择路径");
  206. return;
  207. }
  208. const name = options.value.find(({ value }) => value === pathId.value)!.label;
  209. console.log(pathId.value);
  210. emit("addPath", { name, pathId: pathId.value });
  211. visibleSelectPath.value = false;
  212. };
  213. const getActionDur = (key: string) => {
  214. const actions = amMap[getAMKey(props.am)]?.am?.getSupportActions() || [];
  215. const action = actions.find((item) => item.name === key);
  216. if (action?.duration) {
  217. return Math.floor(action?.duration * 100) / 100;
  218. }
  219. };
  220. </script>
  221. <style scoped lang="scss">
  222. .item {
  223. display: flex;
  224. align-items: center;
  225. justify-content: space-between;
  226. &.action-item {
  227. height: 38px;
  228. background: rgba(255, 255, 255, 0.1);
  229. border-radius: 4px 4px 4px 4px;
  230. margin-bottom: 10px;
  231. padding: 0 10px;
  232. }
  233. }
  234. .pin-position {
  235. position: absolute;
  236. left: 50%;
  237. transform: translate(-50%);
  238. width: 64px;
  239. height: 64px;
  240. background: rgba(27, 27, 28, 0.8);
  241. border-radius: 50%;
  242. bottom: 20px;
  243. z-index: 9;
  244. display: flex;
  245. align-items: center;
  246. justify-content: center;
  247. }
  248. .actions {
  249. display: flex;
  250. flex-wrap: wrap;
  251. div {
  252. width: calc((100% - 28px) / 3);
  253. background: #fff;
  254. margin-top: 7px;
  255. margin-bottom: 7px;
  256. height: 80px;
  257. cursor: pointer;
  258. position: relative;
  259. &::after {
  260. content: "";
  261. position: absolute;
  262. inset: 0;
  263. z-index: 1;
  264. background: rgba(0, 0, 0, 0.5);
  265. }
  266. span {
  267. position: absolute;
  268. z-index: 2;
  269. left: 50%;
  270. top: 50%;
  271. transform: translate(-50%, -50%);
  272. font-size: 16px;
  273. color: #fff;
  274. width: 100%;
  275. text-align: center;
  276. }
  277. &:nth-child(3n - 1) {
  278. margin-left: 14px;
  279. margin-right: 14px;
  280. }
  281. img {
  282. width: 100%;
  283. height: 100%;
  284. object-fit: cover;
  285. }
  286. }
  287. }
  288. </style>
  289. <style lang="scss">
  290. .animation-right .ui-editor-toolbox {
  291. padding-top: 0;
  292. .ant-tabs-tab {
  293. font-size: 16px;
  294. padding: 16px 10px;
  295. }
  296. }
  297. </style>