single.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. <template>
  2. <div v-if="type == 'image'">
  3. <div v-if="myValue != ''">
  4. <ul class="el-upload-list el-upload-list--picture-card">
  5. <li tabindex="0" class="el-upload-list__item is-ready" :style="'width: '+width+'px;height: '+height+'px'">
  6. <div>
  7. <img :src="myValue" alt="" class="el-upload-list__item-thumbnail">
  8. <span class="el-upload-list__item-actions">
  9. <span class="el-upload-list__item-delete" @click="deleteMaterial">
  10. <i class="el-icon-delete" />
  11. </span>
  12. </span>
  13. </div>
  14. </li>
  15. </ul>
  16. </div>
  17. <div v-else tabindex="0" class="el-upload el-upload--picture-card" :style="'width: '+width+'px;height: '+height+'px;'+'line-height:'+height+'px;'" @click="toSeleteMaterial">
  18. <svg-icon icon-class="add" class-name="card-panel-icon" />
  19. </div>
  20. <!-- 查看 -->
  21. <el-dialog
  22. append-to-body
  23. :visible.sync="dialogVisible"
  24. width="35%"
  25. >
  26. <img :src="url" alt="" style="width: 100%">
  27. </el-dialog>
  28. <!-- 素材列表 -->
  29. <el-dialog
  30. title="图片素材库"
  31. append-to-body
  32. :visible.sync="listDialogVisible"
  33. width="70%"
  34. >
  35. <el-container>
  36. <el-aside width="unset">
  37. <div style="margin-bottom: 10px">
  38. <el-button
  39. class="el-icon-plus"
  40. size="small"
  41. @click="materialgroupAdd()"
  42. >
  43. 添加分组
  44. </el-button>
  45. </div>
  46. <div class="group-list">
  47. <div class="group-item" v-for="(group) in materialGroupList">
  48. <el-button @click="selectGroup(group)" type="primary" plain >{{group.name}}</el-button>
  49. </div>
  50. </div>
  51. </el-aside>
  52. <el-main>
  53. <el-card>
  54. <div slot="header">
  55. <el-row>
  56. <el-col :span="12">
  57. <span>{{ materialGroup.name }}</span>
  58. <span v-if="materialGroup.groupId >0">
  59. <el-button size="small" type="text" class="el-icon-edit" style="margin-left: 10px;" @click="materialgroupEdit(materialGroup)">重命名</el-button>
  60. <el-button size="small" type="text" class="el-icon-delete" style="margin-left: 10px;color: red" @click="materialgroupDelete(materialGroup)">删除</el-button>
  61. </span>
  62. </el-col>
  63. <el-col :span="12" style="text-align: right;">
  64. <el-upload
  65. :action="uploadUrl"
  66. :file-list="[]"
  67. :on-progress="handleProgress"
  68. :before-upload="beforeUpload"
  69. :on-success="handleSuccess"
  70. :data="{type: 1}"
  71. multiple
  72. >
  73. <el-button size="small" type="primary">批量上传</el-button>
  74. </el-upload>
  75. </el-col>
  76. </el-row>
  77. </div>
  78. <div v-loading="tableLoading">
  79. <el-alert
  80. v-if="tableData.length <= 0"
  81. title="暂无数据"
  82. type="info"
  83. :closable="false"
  84. center
  85. show-icon
  86. />
  87. <el-row :gutter="5">
  88. <el-checkbox-group v-model="urls" :max="num - myValue.length">
  89. <el-col v-for="(item,index) in tableData" :key="index" :span="4">
  90. <el-card :body-style="{ padding: '5px' }">
  91. <el-image
  92. style="width: 100%;height: 100px"
  93. :src="item.url"
  94. fit="contain"
  95. :preview-src-list="[item.url]"
  96. :z-index="9999"
  97. />
  98. <div>
  99. <el-checkbox class="material-name" :label="item.url">
  100. 选择
  101. </el-checkbox>
  102. <el-row>
  103. <el-col :span="24" class="col-do">
  104. <el-button type="text" size="medium" @click="materialDel(item)">删除</el-button>
  105. </el-col>
  106. </el-row>
  107. </div>
  108. </el-card>
  109. </el-col>
  110. </el-checkbox-group>
  111. </el-row>
  112. <pagination
  113. v-show="total>0"
  114. :total="total"
  115. :page.sync="queryParams.pageNum"
  116. :limit.sync="queryParams.pageSize"
  117. @pagination="getMaterialList"
  118. />
  119. </div>
  120. </el-card>
  121. </el-main>
  122. </el-container>
  123. <span slot="footer" class="dialog-footer">
  124. <el-button @click="listDialogVisible = false">取 消</el-button>
  125. <el-button type="primary" @click="submit">确 定</el-button>
  126. </span>
  127. </el-dialog>
  128. </div>
  129. </template>
  130. <script>
  131. import { listMaterial, getMaterial, delMaterial, addMaterial, updateMaterial, exportMaterial } from "@/api/store/material";
  132. import { getAllMaterialGroup,listMaterialGroup, getMaterialGroup, delMaterialGroup, addMaterialGroup, updateMaterialGroup, exportMaterialGroup } from "@/api/store/materialGroup";
  133. import { Loading } from 'element-ui';
  134. export default {
  135. name: 'ImageSelect',
  136. props: {
  137. // 素材数据
  138. value: {
  139. type: String
  140. },
  141. // 素材类型
  142. type: {
  143. type: String
  144. },
  145. // 素材限制数量,默认5个
  146. num: {
  147. type: Number,
  148. default() {
  149. return 5
  150. }
  151. },
  152. // 宽度
  153. width: {
  154. type: Number,
  155. default() {
  156. return 150
  157. }
  158. },
  159. // 宽度
  160. height: {
  161. type: Number,
  162. default() {
  163. return 150
  164. }
  165. }
  166. },
  167. watch: {
  168. value: function(val) {
  169. this.myValue = val
  170. }
  171. },
  172. data() {
  173. return {
  174. finalQuality:1,
  175. myValue: this.value,
  176. uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
  177. dialogVisible: false,
  178. url: '',
  179. listDialogVisible: false,
  180. materialGroupList: [],
  181. materialGroupLoading: false,
  182. materialGroup:{},
  183. tableData: [],
  184. resultNumber: 0,
  185. total: 0,
  186. queryParams: {
  187. pageNum: 1,
  188. pageSize: 10,
  189. type: null,
  190. groupId: null,
  191. name: null,
  192. url: null,
  193. isDel: null,
  194. createUserId: null,
  195. },
  196. tableLoading: false,
  197. urls: []
  198. }
  199. },
  200. mounted(){
  201. this.getAllMaterialGroup();
  202. },
  203. methods: {
  204. selectGroup(item){
  205. this.materialGroup=item;
  206. this.queryParams.groupId=item.groupId;
  207. this.getMaterialList();
  208. },
  209. materialgroupAdd() {
  210. const that = this
  211. this.$prompt('请输入分组名', '提示', {
  212. confirmButtonText: '确定',
  213. cancelButtonText: '取消'
  214. }).then(({ value }) => {
  215. addMaterialGroup({
  216. name: value
  217. }).then(function() {
  218. that.materialGroup={};
  219. that.getAllMaterialGroup()
  220. })
  221. }).catch(() => {
  222. })
  223. },
  224. materialgroupDelete(materialgroupObj) {
  225. const that = this
  226. this.$confirm('是否确认删除该分组?', '提示', {
  227. confirmButtonText: '确定',
  228. cancelButtonText: '取消',
  229. type: 'warning'
  230. }).then(function() {
  231. delMaterialGroup(materialgroupObj.groupId)
  232. .then(function() {
  233. that.materialGroup={};
  234. that.getAllMaterialGroup()
  235. })
  236. })
  237. },
  238. materialgroupEdit(materialgroupObj) {
  239. const that = this
  240. this.$prompt('请输入分组名', '提示', {
  241. confirmButtonText: '确定',
  242. cancelButtonText: '取消',
  243. inputValue: materialgroupObj.name
  244. }).then(({ value }) => {
  245. updateMaterialGroup({
  246. groupId: materialgroupObj.groupId,
  247. name: value
  248. }).then(function() {
  249. that.materialGroup={};
  250. that.getAllMaterialGroup()
  251. })
  252. }).catch(() => {
  253. })
  254. },
  255. getAllMaterialGroup() {
  256. this.materialGroupLoading = true;
  257. getAllMaterialGroup({}).then(response => {
  258. this.materialGroupList = response.data
  259. console.log(this.materialGroupList)
  260. this.materialGroupLoading = false;
  261. });
  262. },
  263. getMaterialList() {
  264. this.tableLoading = true;
  265. listMaterial(this.queryParams).then(response => {
  266. this.tableData = response.rows;
  267. this.total = response.total;
  268. this.tableLoading = false;
  269. });
  270. },
  271. deleteMaterial() {
  272. const that = this
  273. this.$confirm('是否确认删除?', '提示', {
  274. confirmButtonText: '确定',
  275. cancelButtonText: '取消',
  276. type: 'warning'
  277. }).then(function() {
  278. that.myValue = ''
  279. that.urls = []
  280. })
  281. },
  282. toSeleteMaterial() {
  283. this.listDialogVisible = true
  284. this.getAllMaterialGroup()
  285. this.getMaterialList();
  286. },
  287. materialDel(item) {
  288. const that = this
  289. this.$confirm('是否确认删除该素材?', '提示', {
  290. confirmButtonText: '确定',
  291. cancelButtonText: '取消',
  292. type: 'warning'
  293. }).then(function() {
  294. delMaterial(item.materialId)
  295. .then(function() {
  296. that.queryParams.pageNum=0;
  297. that.getMaterialList();
  298. })
  299. })
  300. },
  301. handleProgress(event, file, fileList) {
  302. },
  303. handleSuccess(response, file, fileList) {
  304. const that = this
  305. addMaterial({
  306. type: '1',
  307. groupId: this.queryParams.groupId,
  308. name: file.name,
  309. url: response.url
  310. }).then(() => {
  311. this.resultNumber++
  312. if (fileList.length === this.resultNumber) {
  313. that.getMaterialList()
  314. this.resultNumber = 0
  315. }
  316. })
  317. },
  318. beforeUpload(file) {
  319. const isPic =
  320. file.type === 'image/jpeg' ||
  321. file.type === 'image/png' ||
  322. file.type === 'image/gif' ||
  323. file.type === 'image/jpg'
  324. const isLt2M = file.size / 1024 / 1024 < 2
  325. if (!isPic) {
  326. this.$message.error('上传图片只能是 JPG、JPEG、PNG、GIF 格式!')
  327. return false
  328. }
  329. return new Promise((resolve, reject) => {
  330. if (file.size / 1024 / 1024 > 3) {
  331. this.$message.error('上传的图片不能超过3MB');
  332. reject();
  333. return;
  334. }
  335. if (file.size / 1024 / 1024 > 1) {
  336. const loadingInstance = Loading.service({ text: '图片内存过大正在压缩图片...' });
  337. // 文件大于1MB时进行压缩
  338. this.compressImage(file).then((compressedFile) => {
  339. loadingInstance.close();
  340. if (compressedFile.size / 1024 > 500) {
  341. this.$message.error('图片压缩后仍大于500KB');
  342. reject();
  343. } else {
  344. // this.$message.success(`图片压缩成功,最终质量为: ${this.finalQuality.toFixed(2)}`);
  345. console.log(`图片压缩成功,最终质量为: ${this.finalQuality.toFixed(2)}`);
  346. console.log(`最终内存大小为: ${(compressedFile.size/1024).toFixed(2)}KB`);
  347. resolve(compressedFile);
  348. }
  349. }).catch((err) => {
  350. loadingInstance.close();
  351. console.error(err);
  352. reject();
  353. });
  354. } else {
  355. resolve(file);
  356. }
  357. // this.loading = this.$loading({
  358. // lock: true,
  359. // text: "上传中",
  360. // background: "rgba(0, 0, 0, 0.7)",
  361. // });
  362. });
  363. },
  364. compressImage(file) {
  365. return new Promise((resolve, reject) => {
  366. const reader = new FileReader();
  367. reader.readAsDataURL(file);
  368. reader.onload = (event) => {
  369. const img = new Image();
  370. img.src = event.target.result;
  371. img.onload = () => {
  372. const canvas = document.createElement('canvas');
  373. const ctx = canvas.getContext('2d');
  374. const width = img.width;
  375. const height = img.height;
  376. canvas.width = width;
  377. canvas.height = height;
  378. ctx.drawImage(img, 0, 0, width, height);
  379. let quality = 1; // 初始压缩质量
  380. let dataURL = canvas.toDataURL('image/jpeg', quality);
  381. // 逐步压缩,直到图片大小小于500KB并且压缩质量不再降低
  382. while (dataURL.length / 1024 > 500 && quality > 0.1) {
  383. quality -= 0.01;
  384. dataURL = canvas.toDataURL('image/jpeg', quality);
  385. }
  386. this.finalQuality = quality; // 存储最终的压缩质量
  387. if (dataURL.length / 1024 > 500) {
  388. reject(new Error('压缩后图片仍然大于500KB'));
  389. return;
  390. }
  391. const arr = dataURL.split(',');
  392. const mime = arr[0].match(/:(.*?);/)[1];
  393. const bstr = atob(arr[1]);
  394. let n = bstr.length;
  395. const u8arr = new Uint8Array(n);
  396. while (n--) {
  397. u8arr[n] = bstr.charCodeAt(n);
  398. }
  399. const compressedFile = new Blob([u8arr], { type: mime });
  400. compressedFile.name = file.name;
  401. resolve(compressedFile);
  402. };
  403. img.onerror = (error) => {
  404. reject(error);
  405. };
  406. };
  407. reader.onerror = (error) => {
  408. reject(error);
  409. };
  410. });
  411. },
  412. submit() {
  413. this.myValue = this.urls[0]
  414. this.$emit('input', this.urls[0])
  415. this.listDialogVisible = false
  416. this.urls = []
  417. }
  418. }
  419. }
  420. </script>
  421. <style rel="stylesheet/scss" lang="scss" scoped>
  422. ::v-deep .el-icon-circle-close{
  423. color: red;
  424. }
  425. .material-name{
  426. padding: 8px 0px;
  427. }
  428. .col-do{
  429. text-align: center;
  430. }
  431. .button-do{
  432. padding: unset!important;
  433. font-size: 12px;
  434. }
  435. .group-list{
  436. display: flex;
  437. flex-direction:column;
  438. align-items: flex-start;
  439. }
  440. .group-item{
  441. margin: 5px;
  442. }
  443. </style>