App.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. <template>
  2. <div class="mirror-setting">
  3. <!-- 图片预览 -->
  4. <el-dialog v-model="dialogVisible">
  5. <img
  6. style="width: 100%"
  7. w-full
  8. :src="dialogImageUrl"
  9. alt="Preview Image"
  10. />
  11. </el-dialog>
  12. <!-- 分镜配置 -->
  13. <div class="project-title">
  14. <el-input
  15. class="title"
  16. type="textarea"
  17. :autosize="{ minRows: 1, maxRows: 4 }"
  18. v-model="project.title"
  19. />
  20. <el-button type="primary" @click="saveProject">保存</el-button>
  21. </div>
  22. <div class="content">
  23. <el-table
  24. :key="data.list.length"
  25. class="main-table"
  26. key="id"
  27. border
  28. v-dragable="dragOptions"
  29. :data="data.list"
  30. header-row-class-name="t-head"
  31. header-cell-class-name="t-cell"
  32. >
  33. <!-- <template v-for="item in columns" :key="item.prop">
  34. <el-table-column :prop="item.prop" :label="item.label" />
  35. 大纲
  36. </template>
  37. :on-preview="handlePictureCardPreview" :on-remove="handleRemove"
  38. -->
  39. <el-table-column prop="name" label="大纲">
  40. <template v-slot="{ row }">
  41. <el-input
  42. type="textarea"
  43. :autosize="{ minRows: 3 }"
  44. v-model="row.name"
  45. :row="3"
  46. placeholder="概括拍摄内容"
  47. />
  48. </template>
  49. </el-table-column>
  50. <el-table-column prop="desc" label="分镜描述">
  51. <template v-slot="{ row }">
  52. <el-input
  53. class="gray"
  54. type="textarea"
  55. :autosize="{ minRows: 3 }"
  56. v-model="row.desc"
  57. :row="3"
  58. placeholder="详细描述分镜"
  59. />
  60. </template>
  61. </el-table-column>
  62. <!-- show-overflow-tooltip -->
  63. <el-table-column prop="clip" label="已拍摄片段">
  64. <template v-slot="{ row }">
  65. <el-upload
  66. ref="upload"
  67. v-model:file-list="row.fileList"
  68. class="list-upload-style"
  69. :class="{
  70. activefileList: row.fileList && row.fileList.length == 1,
  71. }"
  72. list-type="picture-card"
  73. :action="uploadFileUrl"
  74. :on-success="handleUploadSuccess"
  75. :limit="1"
  76. >
  77. <div
  78. class="uploadImg"
  79. v-if="row.fileList && row.fileList.length == 0"
  80. >
  81. <el-icon><Plus /></el-icon>
  82. </div>
  83. <template #file="{ file }">
  84. <div style="width: 100%">
  85. <img
  86. class="el-upload-list__item-thumbnail"
  87. :src="file.url"
  88. alt=""
  89. />
  90. <span class="el-upload-list__item-actions">
  91. <span
  92. class="el-upload-list__item-preview"
  93. @click="handlePictureCardPreview(file)"
  94. >
  95. <el-icon><zoom-in /></el-icon>
  96. </span>
  97. <span
  98. class="el-upload-list__item-delete"
  99. @click="handleRemove(row)"
  100. >
  101. <el-icon><Delete /></el-icon>
  102. </span>
  103. </span>
  104. </div>
  105. </template>
  106. </el-upload>
  107. </template>
  108. </el-table-column>
  109. <el-table-column prop="words" label="台词文案">
  110. <template v-slot="{ row }">
  111. <el-input
  112. class="gray"
  113. type="textarea"
  114. :autosize="{ minRows: 3 }"
  115. v-model="row.words"
  116. placeholder="点击输入台词"
  117. />
  118. </template>
  119. </el-table-column>
  120. <el-table-column prop="marks" label="备注">
  121. <template v-slot="{ row, $index }">
  122. <div class="marksDiv">
  123. <el-input
  124. class="gray"
  125. type="textarea"
  126. :autosize="{ minRows: 3 }"
  127. v-model="row.marks"
  128. placeholder="点击输入内容"
  129. />
  130. <span
  131. class="table-delete"
  132. @click="handleTableRemove($index, row)"
  133. >
  134. <el-icon><Delete /></el-icon>
  135. </span>
  136. </div>
  137. </template>
  138. </el-table-column>
  139. </el-table>
  140. </div>
  141. <div class="add-handle">
  142. <el-button type="primary" @click="handleAdd">
  143. <el-icon class="el-icon--right">
  144. <Plus />
  145. </el-icon>
  146. 添加
  147. <el-input class="add-line" type="text" v-model="addLine" size="small">
  148. </el-input>
  149. </el-button>
  150. </div>
  151. </div>
  152. </template>
  153. <script lang="ts" setup>
  154. import { vDragable } from "./dragable";
  155. import { ElMessage } from "element-plus";
  156. import { reactive, ref, onMounted } from "vue";
  157. import type { UploadFile } from "element-plus";
  158. import { uploadFile as uploadFileUrl } from "@/request";
  159. import {
  160. getCaseScriptInfo,
  161. CaseScriptSaveOrUpdate,
  162. } from "@/app/mirror/store/script";
  163. import linkIco from "@/assets/image/fire.ico";
  164. const link = document.querySelector<HTMLLinkElement>("#app-icon")!;
  165. link.setAttribute("href", linkIco);
  166. const caseId = ref(null);
  167. const project = reactive({
  168. title: "我的脚本",
  169. });
  170. document.title = project.title;
  171. const dialogImageUrl = ref("");
  172. const dialogVisible = ref(false);
  173. const disabled = ref(false);
  174. const addLine = ref(1);
  175. const active = ref(1);
  176. const dragOptions = [
  177. // {
  178. // selector: "thead tr", // add drag support for column
  179. // option: {
  180. // // sortablejs's option
  181. // animation: 150,
  182. // onEnd: (evt) => {
  183. // let oldCol: any = {};
  184. // Object.assign(oldCol, columns.value[evt.oldIndex]);
  185. // columns.value.splice(evt.oldIndex, 1); // 因为新增了数据,所以要移除原来的列的index要在原来的基础上
  186. // setTimeout(() => {
  187. // columns.value.splice(evt.newIndex, 0, oldCol); // 把原来的列数据添加到新的位置,然后再从原位置移除它,触发table的重绘
  188. // }, 30);
  189. // console.log(evt.oldIndex, evt.newIndex);
  190. // },
  191. // },
  192. // },
  193. {
  194. selector: "tbody", // add drag support for row
  195. option: {
  196. // sortablejs's option
  197. animation: 150,
  198. onEnd: (evt: any) => {
  199. // let oldItem = sortList.value[evt.oldIndex];
  200. // let sortLists = sortList.value.filter(
  201. // (_, index) => index !== evt.oldIndex
  202. // );
  203. // sortLists.splice(evt.newIndex, 0, oldItem);
  204. // sortList.value = sortLists;
  205. let list = JSON.parse(JSON.stringify(data.newSortList));
  206. const target = list.splice(evt.oldIndex, 1);
  207. list.splice(evt.newIndex, 0, target[0]);
  208. data.newSortList = list;
  209. console.log(evt.oldIndex, evt.newIndex, data.newSortList, data.list);
  210. },
  211. },
  212. },
  213. ];
  214. const columns = ref([
  215. // { prop: "id", label: "ID", hidden: true, },
  216. { prop: "name", label: "大纲" },
  217. { prop: "desc", label: "分镜描述" },
  218. { prop: "clip", label: "已拍摄片段" },
  219. { prop: "words", label: "台词文案" },
  220. { prop: "marks", label: "备注" },
  221. ]);
  222. const data = reactive({
  223. list: [{ id: 1, name: "", desc: "", fileList: [] }],
  224. newSortList: [],
  225. });
  226. const sortList = ref([0]);
  227. onMounted(() => {
  228. caseId.value = GetRequest("caseId");
  229. getCaseScriptList();
  230. console.log("caseId", caseId); //query传参
  231. });
  232. function getCaseScriptList() {
  233. getCaseScriptInfo(caseId.value)
  234. .then((res) => {
  235. project.title = res.name;
  236. data.list = res.content;
  237. data.newSortList = res.content;
  238. const idList = data.list.map((ele) => ele.id);
  239. active.value = Math.max.apply(null, idList) || 1;
  240. sortList.value = data.list.map((_, index) => index);
  241. })
  242. .catch((err) => {
  243. console.log(err);
  244. });
  245. }
  246. function handleAdd() {
  247. // let content = sortList.value.map((index) => data.list[index]);
  248. // data.list.length = 0;
  249. // Object.assign(data.list, content);
  250. console.log("add", data.newSortList);
  251. for (var i = 1; i <= addLine.value; i++) {
  252. console.log(i);
  253. data.newSortList.push({
  254. id: active.value + 1,
  255. name: "",
  256. desc: "",
  257. fileList: [],
  258. });
  259. }
  260. active.value++;
  261. data.list = data.newSortList;
  262. sortList.value = data.list.map((_, index) => index);
  263. }
  264. const handleRemove = (data) => {
  265. data.fileList = [];
  266. };
  267. const handleTableRemove = (index, datas) => {
  268. data.newSortList = data.newSortList.filter((ele) => ele.id !== datas.id);
  269. console.log("saveProject", data.newSortList);
  270. data.list = data.newSortList;
  271. // let content = sortList.value.map((index) => data.list[index]);
  272. // data.list.length = 0;
  273. // content.splice(index, 1);
  274. // Object.assign(data.list, content);
  275. // sortList.value = content.map((_, index) => index);
  276. console.log("saveProject", index, datas, data.list);
  277. };
  278. const handlePictureCardPreview = (file: UploadFile) => {
  279. dialogImageUrl.value = file.url!;
  280. dialogVisible.value = true;
  281. };
  282. const saveProject = () => {
  283. // let content = sortList.value.map((index) => data.list[index]);
  284. console.log("saveProject", data.list, data.newSortList);
  285. CaseScriptSaveOrUpdate({
  286. caseId: caseId.value,
  287. name: project.title,
  288. content: data.newSortList,
  289. }).then((res) => {
  290. console.log("saveProject");
  291. });
  292. };
  293. function handleUploadSuccess(response: any, uploadFile: UploadFile) {
  294. uploadFile.url = response.data;
  295. console.log(response, uploadFile);
  296. }
  297. function GetRequest(value) {
  298. var url = decodeURI(window.location.search); //?id="123456"&name="www";
  299. var object = {};
  300. if (url.indexOf("?") != -1) {
  301. //url中存在问号,也就说有参数。
  302. var str = url.substr(1); //得到?后面的字符串
  303. var strs = str.split("&"); //将得到的参数分隔成数组[id="123456",name="www"];
  304. for (var i = 0; i < strs.length; i++) {
  305. object[strs[i].split("=")[0]] = strs[i].split("=")[1]; //得到{id:'123456',name:'www'}
  306. }
  307. }
  308. return object[value];
  309. }
  310. </script>
  311. <style lang="scss">
  312. body,
  313. #app {
  314. margin: 0;
  315. padding: 0;
  316. }
  317. .mirror-setting {
  318. width: 100%;
  319. min-height: 100%;
  320. padding-top: 80px;
  321. min-height: calc(100vh - 80px);
  322. margin: 0 auto;
  323. background: #eee;
  324. }
  325. .content {
  326. margin: 0 auto;
  327. display: flex;
  328. padding: 0 40px;
  329. }
  330. .t-head {
  331. border: 1px solid #ddd;
  332. /* padding: 10px; */
  333. /* display: flex; */
  334. position: relative;
  335. background-color: #eee;
  336. }
  337. tbody {
  338. /* border-top: 20px solid transparent; */
  339. }
  340. .t-head th {
  341. margin-bottom: 20px;
  342. }
  343. .project-title {
  344. display: flex;
  345. padding: 0 40px;
  346. align-items: center;
  347. /* justify-content: center; */
  348. }
  349. .project-title .title {
  350. font-size: 28px;
  351. min-height: 0;
  352. height: auto;
  353. background-color: transparent !important;
  354. /* width: 300px; */
  355. margin: 30px 0;
  356. }
  357. .el-textarea__inner {
  358. background-color: transparent;
  359. box-shadow: none;
  360. resize: none;
  361. }
  362. .gray .el-textarea__inner {
  363. background: rgba(227, 225, 225, 0.2);
  364. }
  365. .el-textarea__inner:focus {
  366. box-shadow: none;
  367. }
  368. .el-textarea__inner:hover {
  369. box-shadow: none;
  370. }
  371. .add-handle {
  372. padding: 30px 0;
  373. display: flex;
  374. justify-content: center;
  375. }
  376. .add-line {
  377. margin: 0 10px;
  378. width: 30px;
  379. }
  380. .add-line .el-input__wrapper {
  381. box-shadow: none;
  382. background: rgba(23, 41, 46, 0.2);
  383. }
  384. .add-line input {
  385. color: white;
  386. text-align: center;
  387. }
  388. .activefileList {
  389. .el-upload-list--picture-card {
  390. }
  391. .el-upload--picture-card {
  392. display: none;
  393. }
  394. }
  395. .list-upload-style {
  396. width: 100%;
  397. text-align: center;
  398. .el-upload-list,
  399. .el-upload--text {
  400. width: 100%;
  401. }
  402. .el-upload-list__item-thumbnail,
  403. .el-upload--picture-card {
  404. min-height: 73px;
  405. height: 73px;
  406. width: 100%;
  407. }
  408. .uploadImg,
  409. .el-upload-list__item {
  410. width: 100%;
  411. min-height: 73px;
  412. height: 73px;
  413. line-height: 73px;
  414. .el-upload-list__item-thumbnail {
  415. width: 100%;
  416. object-fit: cover;
  417. }
  418. }
  419. }
  420. .marksDiv {
  421. position: relative;
  422. .table-delete {
  423. position: absolute;
  424. right: -10px;
  425. top: -3px;
  426. }
  427. }
  428. </style>