1
0

App.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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. const caseId = ref(null);
  164. const project = reactive({
  165. title: "我的脚本",
  166. });
  167. const dialogImageUrl = ref("");
  168. const dialogVisible = ref(false);
  169. const disabled = ref(false);
  170. const addLine = ref(1);
  171. const active = ref(1);
  172. const dragOptions = [
  173. // {
  174. // selector: "thead tr", // add drag support for column
  175. // option: {
  176. // // sortablejs's option
  177. // animation: 150,
  178. // onEnd: (evt) => {
  179. // let oldCol: any = {};
  180. // Object.assign(oldCol, columns.value[evt.oldIndex]);
  181. // columns.value.splice(evt.oldIndex, 1); // 因为新增了数据,所以要移除原来的列的index要在原来的基础上
  182. // setTimeout(() => {
  183. // columns.value.splice(evt.newIndex, 0, oldCol); // 把原来的列数据添加到新的位置,然后再从原位置移除它,触发table的重绘
  184. // }, 30);
  185. // console.log(evt.oldIndex, evt.newIndex);
  186. // },
  187. // },
  188. // },
  189. {
  190. selector: "tbody", // add drag support for row
  191. option: {
  192. // sortablejs's option
  193. animation: 150,
  194. onEnd: (evt: any) => {
  195. // let oldItem = sortList.value[evt.oldIndex];
  196. // let sortLists = sortList.value.filter(
  197. // (_, index) => index !== evt.oldIndex
  198. // );
  199. // sortLists.splice(evt.newIndex, 0, oldItem);
  200. // sortList.value = sortLists;
  201. let list = JSON.parse(JSON.stringify(data.newSortList));
  202. const target = list.splice(evt.oldIndex, 1);
  203. list.splice(evt.newIndex, 0, target[0]);
  204. data.newSortList = list
  205. console.log(evt.oldIndex, evt.newIndex,data.newSortList, data.list);
  206. },
  207. },
  208. },
  209. ];
  210. const columns = ref([
  211. // { prop: "id", label: "ID", hidden: true, },
  212. { prop: "name", label: "大纲" },
  213. { prop: "desc", label: "分镜描述" },
  214. { prop: "clip", label: "已拍摄片段" },
  215. { prop: "words", label: "台词文案" },
  216. { prop: "marks", label: "备注" },
  217. ]);
  218. const data = reactive({
  219. list: [{ id: 1, name: "", desc: "", fileList: [] }],
  220. newSortList: [],
  221. });
  222. const sortList = ref([0]);
  223. onMounted(() => {
  224. caseId.value = GetRequest("caseId");
  225. getCaseScriptList();
  226. console.log("caseId", caseId); //query传参
  227. });
  228. function getCaseScriptList() {
  229. getCaseScriptInfo(caseId.value).then((res) => {
  230. project.title = res.name;
  231. data.list = res.content
  232. data.newSortList = res.content
  233. const idList = data.list.map(ele => ele.id);
  234. active.value =Math.max.apply(null,idList) || 1;
  235. sortList.value = data.list.map((_, index) => index);
  236. }).catch((err) => {
  237. console.log(err);
  238. });
  239. }
  240. function handleAdd() {
  241. // let content = sortList.value.map((index) => data.list[index]);
  242. // data.list.length = 0;
  243. // Object.assign(data.list, content);
  244. console.log("add", data.newSortList);
  245. for (var i = 1; i <= addLine.value; i++) {
  246. console.log(i);
  247. data.newSortList.push({ id: active.value + 1, name: "", desc: "", fileList: [] });
  248. }
  249. active.value ++;
  250. data.list = data.newSortList
  251. sortList.value = data.list.map((_, index) => index);
  252. }
  253. const handleRemove = (data) => {
  254. data.fileList = [];
  255. };
  256. const handleTableRemove =(index, datas) => {
  257. data.newSortList = data.newSortList.filter(ele => ele.id !== datas.id);
  258. console.log("saveProject", data.newSortList);
  259. data.list = data.newSortList
  260. // let content = sortList.value.map((index) => data.list[index]);
  261. // data.list.length = 0;
  262. // content.splice(index, 1);
  263. // Object.assign(data.list, content);
  264. // sortList.value = content.map((_, index) => index);
  265. console.log("saveProject", index, datas, data.list);
  266. }
  267. const handlePictureCardPreview = (file: UploadFile) => {
  268. dialogImageUrl.value = file.url!;
  269. dialogVisible.value = true;
  270. };
  271. const saveProject = () => {
  272. // let content = sortList.value.map((index) => data.list[index]);
  273. console.log("saveProject", data.list, data.newSortList);
  274. CaseScriptSaveOrUpdate({
  275. caseId: caseId.value,
  276. name: project.title,
  277. content: data.newSortList,
  278. }).then((res) => {
  279. console.log("saveProject");
  280. });
  281. };
  282. function handleUploadSuccess(response: any, uploadFile: UploadFile) {
  283. uploadFile.url = response.data;
  284. console.log(response, uploadFile);
  285. }
  286. function GetRequest(value) {
  287. var url = decodeURI(window.location.search); //?id="123456"&name="www";
  288. var object = {};
  289. if (url.indexOf("?") != -1) {
  290. //url中存在问号,也就说有参数。
  291. var str = url.substr(1); //得到?后面的字符串
  292. var strs = str.split("&"); //将得到的参数分隔成数组[id="123456",name="www"];
  293. for (var i = 0; i < strs.length; i++) {
  294. object[strs[i].split("=")[0]] = strs[i].split("=")[1]; //得到{id:'123456',name:'www'}
  295. }
  296. }
  297. return object[value];
  298. }
  299. </script>
  300. <style lang="scss">
  301. body,
  302. #app {
  303. margin: 0;
  304. padding: 0;
  305. }
  306. .mirror-setting {
  307. width: 100%;
  308. min-height: 100%;
  309. padding-top: 80px;
  310. min-height: calc(100vh - 80px);
  311. margin: 0 auto;
  312. background: #eee;
  313. }
  314. .content {
  315. margin: 0 auto;
  316. display: flex;
  317. padding: 0 40px;
  318. }
  319. .t-head {
  320. border: 1px solid #ddd;
  321. /* padding: 10px; */
  322. /* display: flex; */
  323. position: relative;
  324. background-color: #eee;
  325. }
  326. tbody {
  327. /* border-top: 20px solid transparent; */
  328. }
  329. .t-head th {
  330. margin-bottom: 20px;
  331. }
  332. .project-title {
  333. display: flex;
  334. padding: 0 40px;
  335. align-items: center;
  336. /* justify-content: center; */
  337. }
  338. .project-title .title {
  339. font-size: 28px;
  340. min-height: 0;
  341. height: auto;
  342. background-color: transparent !important;
  343. /* width: 300px; */
  344. margin: 30px 0;
  345. }
  346. .el-textarea__inner {
  347. background-color: transparent;
  348. box-shadow: none;
  349. resize: none;
  350. }
  351. .gray .el-textarea__inner {
  352. background: rgba(227, 225, 225, 0.2);
  353. }
  354. .el-textarea__inner:focus {
  355. box-shadow: none;
  356. }
  357. .el-textarea__inner:hover {
  358. box-shadow: none;
  359. }
  360. .add-handle {
  361. padding: 30px 0;
  362. display: flex;
  363. justify-content: center;
  364. }
  365. .add-line {
  366. margin: 0 10px;
  367. width: 30px;
  368. }
  369. .add-line .el-input__wrapper {
  370. box-shadow: none;
  371. background: rgba(23, 41, 46, 0.2);
  372. }
  373. .add-line input {
  374. color: white;
  375. text-align: center;
  376. }
  377. .activefileList {
  378. .el-upload-list--picture-card {
  379. }
  380. .el-upload--picture-card {
  381. display: none;
  382. }
  383. }
  384. .list-upload-style {
  385. width: 100%;
  386. text-align: center;
  387. .el-upload-list,
  388. .el-upload--text {
  389. width: 100%;
  390. }
  391. .el-upload-list__item-thumbnail,
  392. .el-upload--picture-card {
  393. min-height: 73px;
  394. height: 73px;
  395. width: 100%;
  396. }
  397. .uploadImg,
  398. .el-upload-list__item {
  399. width: 100%;
  400. min-height: 73px;
  401. height: 73px;
  402. line-height: 73px;
  403. .el-upload-list__item-thumbnail {
  404. width: 100%;
  405. object-fit: cover;
  406. }
  407. }
  408. }
  409. .marksDiv{
  410. position: relative;
  411. .table-delete{
  412. position: absolute;
  413. right: -10px;
  414. top: -3px;
  415. }
  416. }
  417. </style>