detail.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <template>
  2. <div class="container" v-loading="loading">
  3. <el-form
  4. :model="form"
  5. :rules="rules"
  6. ref="form"
  7. label-position="right"
  8. label-width="118px">
  9. <div class="flexr">
  10. <div class="content flex1">
  11. <div class="section-title">Article Detail</div>
  12. <el-form-item
  13. prop="articleTypeId"
  14. label="Article Type:">
  15. <el-select
  16. v-model="form.articleTypeId"
  17. class="add-text"
  18. placeholder="">
  19. <el-option
  20. v-for="(item,index) in options.type"
  21. :key="index"
  22. :label="item.name"
  23. :value="item.value"/>
  24. </el-select>
  25. </el-form-item>
  26. <el-form-item
  27. prop="articleTitle"
  28. label="Article Title:">
  29. <el-input
  30. v-model="form.articleTitle"
  31. class="add-text"
  32. placeholder=""
  33. maxlength="100"/>
  34. </el-form-item>
  35. <!-- <el-form-item
  36. label="Posted Date:"
  37. v-if="isEdit">
  38. <el-input
  39. v-model="form.createTime"
  40. class="add-text"
  41. readonly/>
  42. </el-form-item> -->
  43. <el-form-item
  44. prop="articleContent"
  45. label=""
  46. label-width="0">
  47. <label
  48. class="el-form-item__label"
  49. style="width: 118px;">Article Content:</label>
  50. <el-input
  51. v-model="form.articleContent"
  52. class="area-text"
  53. type="textarea"
  54. placeholder=""
  55. maxlength="5000"
  56. :autosize="autoSize"/>
  57. </el-form-item>
  58. </div>
  59. <div class="content flex1">
  60. <div class="section-title">Images & Links</div>
  61. <el-form-item
  62. label=""
  63. label-width="0">
  64. <label
  65. class="el-form-item__label"
  66. style="float: none;">Photos (Max 4 photos and 2mb each)</label>
  67. <div class="flexcr">
  68. <el-upload
  69. class="logo-upload"
  70. action
  71. :limit="1"
  72. :show-file-list="false"
  73. :file-list="[]"
  74. :http-request="file => uploadImages(file, index)"
  75. accept=".jpg,.jpeg,.png,.gif,.JPG,.JPEG"
  76. v-loading="item.loading"
  77. v-for="(item, index) in images">
  78. <div class="uploader-image" v-if="item.articleImagePath">
  79. <el-image
  80. :src="$imageSrc(item.articleImagePath)"
  81. title="Click to update image"/>
  82. <i
  83. title="Click to delete image"
  84. class="uploader-del el-icon-delete"
  85. @click.stop="deleteImages(index)"></i>
  86. </div>
  87. <i v-else
  88. class="el-icon-plus avatar-uploader-icon"
  89. title="Click to select file"/>
  90. </el-upload>
  91. </div>
  92. </el-form-item>
  93. <el-form-item
  94. label=""
  95. label-width="0">
  96. <label
  97. class="el-form-item__label"
  98. style="float: none;">LINKS:</label>
  99. <div style="max-width: 600px; display: grid;">
  100. <el-table
  101. class="article-links-table no-border"
  102. :data="form.articleLinks">
  103. <el-table-column
  104. label="Name Of Link"
  105. min-width="120px">
  106. <template slot-scope="{row}">
  107. <el-input
  108. v-model="row.articleLinkName"
  109. maxlength="100"/>
  110. </template>
  111. </el-table-column>
  112. <el-table-column
  113. label="Hyperlink"
  114. min-width="130px">
  115. <template slot-scope="{row}">
  116. <el-input
  117. v-model="row.articleLink"
  118. maxlength="150"/>
  119. </template>
  120. </el-table-column>
  121. <el-table-column
  122. label=""
  123. width="90px">
  124. <template slot-scope="{row,$index}">
  125. <div class="flexc" v-loading="row.loading">
  126. <i
  127. class="list-item-icon el-icon-remove-outline"
  128. style="color: #ED3F3F;"
  129. @click="onRemoveLink($index)"/>
  130. <i
  131. v-if="$index === form.articleLinks.length - 1"
  132. class="list-item-icon el-icon-circle-plus-outline"
  133. style="color: #82CF08;"
  134. @click="addLinkObj"/>
  135. </div>
  136. </template>
  137. </el-table-column>
  138. </el-table>
  139. </div>
  140. </el-form-item>
  141. </div>
  142. </div>
  143. <div class="content flexcr">
  144. <div class="buttons">
  145. <el-button
  146. @click="onClickCancel"
  147. type="primary"
  148. class="cancel-button">
  149. Cancel
  150. </el-button>
  151. <el-button
  152. @click="onClickSave"
  153. type="primary">
  154. Save
  155. </el-button>
  156. </div>
  157. <div
  158. class="update-by"
  159. v-if="isEdit">
  160. <span
  161. class="add-text"
  162. :title='"CREATED BY " + form.createdBy + " ON " + form.createdOn'>
  163. LAST UPDATED BY {{form.updatedBy}} TIMESTAMP: {{form.updatedOn}}
  164. </span>
  165. </div>
  166. </div>
  167. </el-form>
  168. </div>
  169. </template>
  170. <script>
  171. import api from '../../api/article.js'
  172. export default {
  173. data() {
  174. return {
  175. loading: false,
  176. form: {
  177. articleTypeId: "",
  178. articleTitle: "",
  179. articleContent: "",
  180. articleImagePaths: [],
  181. articleLinks: [],
  182. createTime: ""
  183. },
  184. options: {
  185. type: []
  186. },
  187. images: [{
  188. articleImagePath: "",
  189. loading: false
  190. }],
  191. isEdit: false,
  192. rules: {
  193. articleTypeId: [{
  194. message: "Please select article type",
  195. trigger: "change",
  196. required: true,
  197. }],
  198. articleTitle: [{
  199. message: "Please input article title",
  200. trigger: "blur",
  201. required: true,
  202. }],
  203. articleContent: [{
  204. message: "Please input article content",
  205. trigger: "blur",
  206. required: true,
  207. }],
  208. },
  209. autoSize: {
  210. minRows: 20,
  211. maxRows: 50
  212. }
  213. };
  214. },
  215. created() {
  216. this.loading = true;
  217. this.addLinkObj();
  218. if (this.$route.params.id) {
  219. this.isEdit = true;
  220. }
  221. this.getArticleTypeOptions();
  222. },
  223. methods: {
  224. onClickCancel() {
  225. this.$nextTick(() => {
  226. this.$router.replace({
  227. path: "/marketing-management/article"
  228. })
  229. })
  230. },
  231. getArticleTypeOptions() {
  232. api.getArticleTypeOption().then(res => {
  233. this.options.type = res.data;
  234. }).catch(err => {
  235. this.$message.error(err);
  236. }).finally(() => {
  237. if (this.isEdit) {
  238. this.getArticleDetail()
  239. } else {
  240. this.loading = false;
  241. }
  242. });
  243. },
  244. getArticleDetail() {
  245. api.viewArticle(this.$route.params.id).then(res => {
  246. if (res.data) {
  247. this.form = res.data
  248. this.images = []
  249. this.form.articleImagePaths.forEach(item => {
  250. this.images.push({
  251. ...item,
  252. loading: false
  253. })
  254. })
  255. this.addImageObj();
  256. }
  257. }).catch(err => {
  258. this.$message.error(err)
  259. }).finally(() => {
  260. this.loading = false;
  261. })
  262. },
  263. addImageObj() {
  264. if (this.images.length < 4) {
  265. this.images.push({
  266. articleImagePath: "",
  267. loading: false
  268. })
  269. }
  270. },
  271. uploadImages(file, index) {
  272. this.images[index].loading = true;
  273. const formData = new FormData()
  274. formData.append('file', file.file)
  275. api.uploadImages(formData).then(res => {
  276. if (res.data.picturePath) {
  277. if (!this.images[index].articleImagePath) {
  278. this.addImageObj();
  279. }
  280. this.images[index].articleImagePath = res.data.picturePath
  281. }
  282. }).catch(err => {
  283. this.$message({
  284. message: err,
  285. type: 'error'
  286. })
  287. }).finally(() => {
  288. this.images[index].loading = false;
  289. })
  290. },
  291. deleteImages(index) {
  292. const item = this.images[index];
  293. if (item.articleImageId) {
  294. this.$confirm('Confirm delete?', 'Delete', {
  295. confirmButtonText: 'Confirm',
  296. cancelButtonText: 'Cancel',
  297. type: 'warning'
  298. }).then(res => {
  299. this.deleteImageById(item.articleImageId, index);
  300. })
  301. } else {
  302. this.images.splice(index, 1);
  303. this.addImageObj()
  304. }
  305. },
  306. deleteImageById(id, index) {
  307. this.images[index].loading = true;
  308. api.deleteArticleImage(id).then(res => {
  309. this.images.splice(index, 1);
  310. this.addImageObj()
  311. }).catch(err => {
  312. this.$message({
  313. message: err,
  314. type: 'error'
  315. })
  316. this.images[index].loading = false;
  317. })
  318. },
  319. addLinkObj() {
  320. this.form.articleLinks.push({
  321. loading: false,
  322. articleLink: "",
  323. articleLinkName: ""
  324. })
  325. },
  326. onRemoveLink(index) {
  327. const item = this.form.articleLinks[index];
  328. if (item.articleLinkId) {
  329. this.$confirm('Confirm delete?', 'Delete', {
  330. confirmButtonText: 'Confirm',
  331. cancelButtonText: 'Cancel',
  332. type: 'warning'
  333. }).then(res => {
  334. this.deleteLinkById(item.articleLinkId, index);
  335. })
  336. } else {
  337. this.form.articleLinks.splice(index, 1);
  338. if (this.form.articleLinks.length == 0) {
  339. this.addLinkObj()
  340. }
  341. }
  342. },
  343. deleteLinkById(id, index) {
  344. this.form.articleLinks[index].loading = true;
  345. api.deleteArticleLink(id).then(res => {
  346. this.form.articleLinks.splice(index, 1);
  347. if (this.form.articleLinks.length == 0) {
  348. this.addLinkObj()
  349. }
  350. }).catch(err => {
  351. this.$message({
  352. message: err,
  353. type: 'error'
  354. })
  355. this.form.articleLinks[index].loading = false;
  356. })
  357. },
  358. onClickSave() {
  359. this.$refs['form'].validate((valid) => {
  360. if (valid) {
  361. const images = this.images.filter(item => item.articleImagePath)
  362. if (images.length == 0) {
  363. this.$message({
  364. message: "Please upload at least one image",
  365. type: 'error'
  366. })
  367. return;
  368. }
  369. const links = this.form.articleLinks.filter(item => item.articleLinkName);
  370. if (links.length == 0) {
  371. this.$message({
  372. message: "Please add at least one link",
  373. type: 'error'
  374. })
  375. return;
  376. }
  377. const params = {
  378. ...this.form,
  379. articleLinks: links,
  380. articleImagePaths: images
  381. }
  382. this.loading = true;
  383. this.isEdit ? this.updateArticle(params) : this.addArticle(params);
  384. }
  385. })
  386. },
  387. addArticle(params) {
  388. api.addArticle(params).then(res => {
  389. this.$message({
  390. type: 'success',
  391. message: "Add successfully"
  392. });
  393. this.onClickCancel();
  394. }).catch(err => {
  395. this.loading = false;
  396. this.$message({
  397. type: 'error',
  398. message: err
  399. })
  400. });
  401. },
  402. updateArticle(params) {
  403. api.updateArticle(params).then(res => {
  404. this.$message({
  405. type: 'success',
  406. message: "Update successfully"
  407. });
  408. this.onClickCancel();
  409. }).catch(err => {
  410. this.loading = false;
  411. this.$message({
  412. type: 'error',
  413. message: err
  414. })
  415. });
  416. }
  417. }
  418. }
  419. </script>
  420. <style lang="scss" scoped>
  421. @import '../../styles/variables.scss';
  422. .container {
  423. width: 100%;
  424. padding: 20px 60px;
  425. min-height: $mainAppMinHeight;
  426. background-color: #F0F5FC;
  427. }
  428. .content {
  429. min-width: 400px;
  430. margin: 0 8px 16px;
  431. padding: 15px 60px;
  432. border-radius: 6px;
  433. background-color: white;
  434. }
  435. .section-title {
  436. color: #333;
  437. margin-top: 20px;
  438. margin-bottom: 30px;
  439. font-size: 15px;
  440. user-select: none;
  441. line-height: 24px;
  442. font-weight: bold;
  443. font-family: sans-serif;
  444. text-transform: uppercase;
  445. }
  446. .add-text {
  447. width: 100%;
  448. min-width: 200px;
  449. }
  450. .area-text {
  451. width: 100%;
  452. min-width: 200px;
  453. }
  454. .add-text ::v-deep .el-textarea__inner,
  455. .area-text ::v-deep .el-textarea__inner {
  456. font-family: sans-serif;
  457. }
  458. .hr {
  459. height: 2px;
  460. margin: 10px -40px;
  461. background-color: #F0F5FC;
  462. }
  463. .buttons {
  464. padding-top: 15px;
  465. padding-bottom: 15px;
  466. }
  467. @media screen and (max-width: 500px) {
  468. .container {
  469. padding: 0px;
  470. }
  471. .content {
  472. padding: 15px 30px;
  473. }
  474. }
  475. .logo-upload {
  476. width: 148px;
  477. height: 148px;
  478. position: relative;
  479. margin-right: 15px;
  480. margin-bottom: 15px;
  481. ::v-deep .el-image {
  482. width: 148px;
  483. height: 148px;
  484. border-radius: 6px;
  485. }
  486. }
  487. .avatar-uploader-icon {
  488. width: 148px;
  489. height: 148px;
  490. color: #8c939d;
  491. cursor: pointer;
  492. font-size: 28px;
  493. text-align: center;
  494. line-height: 148px;
  495. border-radius: 6px;
  496. border: 1px dashed #d9d9d9;
  497. }
  498. .uploader-image {
  499. width: 148px;
  500. height: 148px;
  501. text-align: left;
  502. position: relative;
  503. }
  504. .uploader-image ::v-deep img {
  505. object-fit: cover;
  506. }
  507. .uploader-del {
  508. right: 4px;
  509. bottom: 4px;
  510. color: #fff;
  511. padding: 5px;
  512. display: none;
  513. border-radius: 30px;
  514. position: absolute;
  515. background-color: rgba(1,1,1,.4);
  516. }
  517. .uploader-image:hover .uploader-del {
  518. display: block;
  519. }
  520. .article-links-table ::v-deep .el-table__header th {
  521. color: #606266;
  522. border: none;
  523. padding: 10px 0 0;
  524. font-weight: normal;
  525. background: transparent;
  526. }
  527. .article-links-table ::v-deep td {
  528. border: none;
  529. padding: 1px 0;
  530. background: #fff !important;
  531. }
  532. .article-links-table ::v-deep .cell {
  533. padding: 0px 8px 10px;
  534. }
  535. .list-item-icon {
  536. height: 30px;
  537. cursor: pointer;
  538. font-size: 26px;
  539. font-weight: 500;
  540. line-height: 30px;
  541. }
  542. .list-item-icon + .list-item-icon {
  543. margin-left: 15px;
  544. }
  545. </style>