index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. <template>
  2. <div class="p-2">
  3. <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
  4. :leave-active-class="proxy?.animate.searchAnimate.leave">
  5. <div class="search" v-show="showSearch">
  6. <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
  7. <!-- <el-form-item label="url" prop="url">
  8. <el-input v-model="queryParams.url" placeholder="请输入url" clearable @keyup.enter="handleQuery" />
  9. </el-form-item>
  10. <el-form-item label="名称" prop="fileName">
  11. <el-input v-model="queryParams.fileName" placeholder="请输入名称" clearable @keyup.enter="handleQuery" />
  12. </el-form-item> -->
  13. <el-form-item label="图片类型" prop="imgType">
  14. <el-select v-model="form.imgType" placeholder="图片类型" clearable>
  15. <el-option v-for="dict in picture_type" :label="dict.label" :value="dict.value" :key="dict.value" />
  16. </el-select>
  17. </el-form-item>
  18. <el-form-item label="主图标识" prop="enumC">
  19. <el-select v-model="form.enumC" placeholder="图片类型" clearable>
  20. <el-option v-for="dict in enum_type" :label="dict.label" :value="dict.value" :key="dict.value" />
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item>
  24. <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
  25. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  26. </el-form-item>
  27. </el-form>
  28. </div>
  29. </transition>
  30. <el-card shadow="never">
  31. <template #header>
  32. <el-row :gutter="10" class="mb8">
  33. <el-col :span="1.5">
  34. <el-button type="primary" plain icon="Plus" @click="handleAdd"
  35. v-hasPermi="['system:img:add']">新增</el-button>
  36. </el-col>
  37. <el-col :span="1.5">
  38. <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
  39. v-hasPermi="['system:img:edit']">修改</el-button>
  40. </el-col>
  41. <el-col :span="1.5">
  42. <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
  43. v-hasPermi="['system:img:remove']">删除</el-button>
  44. </el-col>
  45. <el-col :span="1.5">
  46. <el-button type="warning" plain icon="Download" @click="handleExport"
  47. v-hasPermi="['system:img:export']">导出</el-button>
  48. </el-col>
  49. <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
  50. </el-row>
  51. </template>
  52. <el-table v-loading="loading" :data="imgList" @selection-change="handleSelectionChange">
  53. <el-table-column type="selection" width="55" align="center" />
  54. <el-table-column label="图片id" align="center" prop="ossId" v-if="false" />
  55. <el-table-column label="图片类型" align="center" prop="imgType" />
  56. <el-table-column label="主图展示" align="center" prop="url">
  57. <template #default="scope">
  58. <ImagePreview :width="100" :height="100" :src="scope.row.url" :preview-src-list="[scope.row.url]" />
  59. </template>
  60. </el-table-column>
  61. <el-table-column label="名称" align="center" prop="fileName" />
  62. <el-table-column label="主图标识" align="center" prop="enumC" />
  63. <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  64. <template #default="scope">
  65. <el-tooltip content="修改" placement="top">
  66. <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
  67. v-hasPermi="['system:img:edit']"></el-button>
  68. </el-tooltip>
  69. <el-tooltip content="删除" placement="top">
  70. <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
  71. v-hasPermi="['system:img:remove']"></el-button>
  72. </el-tooltip>
  73. </template>
  74. </el-table-column>
  75. </el-table>
  76. <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
  77. v-model:limit="queryParams.pageSize" @pagination="getList" />
  78. </el-card>
  79. <!-- 添加或修改图片对话框 -->
  80. <el-dialog :title="dialog.title" v-model="dialog.visible" width="1000px" append-to-body>
  81. <el-form ref="imgFormRef" :model="form" :rules="rules" label-width="80px">
  82. <!-- <el-form-item label="url" prop="url">
  83. <el-input v-model="form.url" placeholder="请输入url" />
  84. </el-form-item>
  85. <el-form-item label="名称" prop="fileName">
  86. <el-input v-model="form.fileName" placeholder="请输入名称" />
  87. </el-form-item> -->
  88. <el-form-item label="图片类型" prop="imgType">
  89. <el-select v-model="form.imgType" placeholder="图片类型" clearable style="width:300px" @change="handleChange">
  90. <el-option v-for="dict in picture_type" :label="dict.label" :value="dict.value" :key="dict.value" />
  91. </el-select>
  92. </el-form-item>
  93. <el-form-item label="位置" prop="positionId" v-show="showPosition">
  94. <el-select v-model="form.positionId" filterable placeholder="请选择" style="width: 200px">
  95. <el-option v-for="item in positionList" :key="item" :label="item.positionName" :value="item.positionId"> </el-option>
  96. </el-select>
  97. </el-form-item>
  98. <!-- <el-form-item label="主图标识" prop="enumC">
  99. <el-select v-model="form.enumC" placeholder="主图标识" clearable style="width:500px">
  100. <el-option v-for="dict in enum_type" :label="dict.label" :value="dict.value" :key="dict.value" />
  101. </el-select>
  102. </el-form-item> -->
  103. <el-upload v-model:file-list="fileList" :multiple="true" :action="upload.url" :on-preview="handlePreview"
  104. :on-remove="handleRemove" :before-remove="beforeRemove" :headers="upload.headers"
  105. :on-success="handleUploadSuccess" :before-upload="handleBeforeUpload" list-type="picture-card">
  106. 上传图片
  107. <template #tip>
  108. <div class="el-upload__tip">
  109. 小于500KB的jpg/png/jpeg文件
  110. </div>
  111. </template>
  112. <template #file="{ file }">
  113. <div>
  114. <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
  115. <span class="el-upload-list__item-actions">
  116. <span @click="onClickDefaultPhoto(file)">
  117. <el-icon v-if="file.defaults">
  118. <Open />
  119. </el-icon>
  120. <el-icon v-else>
  121. <TurnOff />
  122. </el-icon>
  123. </span>
  124. <!-- <span @click="() => { }">
  125. <el-icon>
  126. <zoom-in />
  127. </el-icon>
  128. </span> -->
  129. <span v-if="!disabled" @click="onClickDeletePhoto(file)">
  130. <el-icon>
  131. <Delete />
  132. </el-icon>
  133. </span>
  134. </span>
  135. </div>
  136. </template>
  137. </el-upload>
  138. </el-form>
  139. <template #footer>
  140. <div class="dialog-footer">
  141. <el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
  142. <el-button @click="cancel">取 消</el-button>
  143. </div>
  144. </template>
  145. </el-dialog>
  146. </div>
  147. </template>
  148. <script setup name="Img" lang="ts">
  149. import { listImg, getImg, delImg, addImg, updateImg, listPosition } from '@/api/system/img';
  150. import { ImgVO, ImgQuery, ImgForm } from '@/api/system/img/types';
  151. import type { UploadProps } from 'element-plus'
  152. import { globalHeaders } from "@/utils/request";
  153. import { propTypes } from '@/utils/propTypes';
  154. import { any } from 'vue-types';
  155. const number = ref(0);
  156. const fileList = ref<any[]>([]);
  157. const disabled = ref(false)
  158. const upload = reactive<UploadOption>({
  159. headers: globalHeaders(),
  160. url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload',
  161. });
  162. const props = defineProps({
  163. modelValue: [String, Object, Array],
  164. // 数量限制
  165. limit: propTypes.number.def(5),
  166. // 大小限制(MB)
  167. fileSize: propTypes.number.def(5),
  168. // 文件类型, 例如['png', 'jpg', 'jpeg']
  169. fileType: propTypes.array.def(["png", "jpg", "jpeg"]),
  170. // 是否显示提示
  171. isShowTip: propTypes.bool.def(true),
  172. });
  173. interface PositionVO {
  174. positionId: string;
  175. positionName: string;
  176. }
  177. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  178. const { picture_type } = toRefs<any>(proxy?.useDict("picture_type"));
  179. const { enum_type } = toRefs<any>(proxy?.useDict("enum_type"));
  180. const imgList = ref<ImgVO[]>([]);
  181. const buttonLoading = ref(false);
  182. const loading = ref(true);
  183. const showSearch = ref(true);
  184. const ids = ref<Array<string | number>>([]);
  185. const single = ref(true);
  186. const multiple = ref(true);
  187. const total = ref(0);
  188. const emit = defineEmits(['update:modelValue']);
  189. const queryFormRef = ref<ElFormInstance>();
  190. const imgFormRef = ref<ElFormInstance>();
  191. const positionList = ref<PositionVO[]>([]);
  192. const showPosition = ref(false);
  193. const dialog = reactive<DialogOption>({
  194. visible: false,
  195. title: ''
  196. });
  197. // 上传失败
  198. const handleUploadError = () => {
  199. proxy?.$modal.msgError("上传文件失败");
  200. }
  201. const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
  202. console.log(file, uploadFiles)
  203. }
  204. const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
  205. console.log(uploadFile)
  206. }
  207. const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
  208. return ElMessageBox.confirm(
  209. `Cancel the transfer of ${uploadFile.name} ?`
  210. ).then(
  211. () => true,
  212. () => false
  213. )
  214. }
  215. // 上传前校检格式和大小
  216. const handleBeforeUpload = (file: any) => {
  217. // 校检文件类型
  218. if (props.fileType.length) {
  219. const fileName = file.name.split('.');
  220. const fileExt = fileName[fileName.length - 1];
  221. const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
  222. if (!isTypeOk) {
  223. proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
  224. return false;
  225. }
  226. }
  227. // 校检文件大小
  228. if (props.fileSize) {
  229. const isLt = file.size / 1024 / 1024 < props.fileSize;
  230. if (!isLt) {
  231. proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
  232. return false;
  233. }
  234. }
  235. proxy?.$modal.loading("正在上传文件,请稍候...");
  236. number.value++;
  237. return true;
  238. }
  239. const listImgInfo: any[] = [];
  240. const handleUploadSuccess = (res: any) => {
  241. // 如果上传成功
  242. if (res.code === 200) {
  243. proxy?.$modal.closeLoading();
  244. } else {
  245. proxy?.$modal.loading(res.msg);
  246. proxy?.$modal.closeLoading();
  247. }
  248. }
  249. const onClickDefaultPhoto = (file: any) => {
  250. if (file.defaults) {
  251. file.defaults = false;
  252. file.enumC = '1'; //主图标识
  253. } else {
  254. file.defaults = true;
  255. file.enumC = '0'; //主图标识
  256. fileList.value.forEach(item => {
  257. if (item.uid !== file.uid) {
  258. item.defaults = false;
  259. file.enumC = '1'; //主图标识
  260. }
  261. })
  262. }
  263. }
  264. const onClickDeletePhoto = (file: any) => {
  265. const index = fileList.value.findIndex((item) => {
  266. return item.uid === file.uid;
  267. })
  268. fileList.value.splice(index, 1);
  269. emit("update:modelValue", listToString(fileList.value));
  270. }
  271. const initFormData: ImgForm = {
  272. ossId: undefined,
  273. imgType: undefined,
  274. enumC: undefined,
  275. sysimg: undefined,
  276. positionId: undefined,
  277. }
  278. const data = reactive<PageData<ImgForm, ImgQuery>>({
  279. form: { ...initFormData },
  280. queryParams: {
  281. pageNum: 1,
  282. pageSize: 10,
  283. imgType: undefined,
  284. enumC: undefined,
  285. params: {
  286. }
  287. },
  288. rules: {
  289. // ossId: [
  290. // { required: true, message: "图片id不能为空", trigger: "blur" }
  291. // ],
  292. imgType: [
  293. { required: true, message: "图片类型不能为空", trigger: "change" }
  294. ],
  295. }
  296. });
  297. const { queryParams, form, rules } = toRefs(data);
  298. /** 查询图片列表 */
  299. const getList = async () => {
  300. console.info(queryParams.value, 'queryParams.value');
  301. loading.value = true;
  302. const res = await listImg(queryParams.value);
  303. imgList.value = res.rows;
  304. total.value = res.total;
  305. loading.value = false;
  306. }
  307. /** 取消按钮 */
  308. const cancel = () => {
  309. reset();
  310. dialog.visible = false;
  311. }
  312. /** 表单重置 */
  313. const reset = () => {
  314. form.value = { ...initFormData };
  315. imgFormRef.value?.resetFields();
  316. }
  317. /** 搜索按钮操作 */
  318. const handleQuery = () => {
  319. queryParams.value.pageNum = 1;
  320. queryParams.value.enumC = form.value.enumC;
  321. queryParams.value.imgType = form.value.imgType;
  322. getList();
  323. }
  324. /** 重置按钮操作 */
  325. const resetQuery = () => {
  326. queryFormRef.value?.resetFields();
  327. handleQuery();
  328. }
  329. /** 多选框选中数据 */
  330. const handleSelectionChange = (selection: ImgVO[]) => {
  331. ids.value = selection.map(item => item.ossId);
  332. single.value = selection.length != 1;
  333. multiple.value = !selection.length;
  334. }
  335. /** 新增按钮操作 */
  336. const handleAdd = () => {
  337. reset();
  338. dialog.visible = true;
  339. dialog.title = "添加图片";
  340. }
  341. /** 修改按钮操作 */
  342. const handleUpdate = async (row?: ImgVO) => {
  343. reset();
  344. const _imgId = row?.ossId || ids.value[0]
  345. const res = await getImg(_imgId);
  346. fileList.value = [];
  347. const imgData = { fileName: res.data.fileName, e: res.data.enumC, url: res.data.url, ossId: res.data.ossId };
  348. fileList.value.push(imgData);
  349. console.info(fileList.value, 'fileList.value');
  350. fileList.value.forEach(item => {
  351. if (item.e === '0') {
  352. item.defaults = true;
  353. }
  354. })
  355. if(res.data.imgType === '0'){
  356. showPosition.value = true;
  357. }
  358. Object.assign(form.value, res.data);
  359. getPositionList();
  360. dialog.visible = true;
  361. dialog.title = "修改图片";
  362. }
  363. /** 提交按钮 */
  364. const submitForm = () => {
  365. imgFormRef.value?.validate(async (valid: boolean) => {
  366. if (valid) {
  367. buttonLoading.value = true;
  368. data.form.sysimg = fileList.value;
  369. console.info(data.form.sysimg, 'data.form.sysimg');
  370. if (form.value.ossId) {
  371. await updateImg(form.value).finally(() => buttonLoading.value = false);
  372. } else {
  373. await addImg(form.value).finally(() => buttonLoading.value = false);
  374. }
  375. proxy?.$modal.msgSuccess("修改成功");
  376. dialog.visible = false;
  377. await getList();
  378. }
  379. });
  380. }
  381. /** 删除按钮操作 */
  382. const handleDelete = async (row?: ImgVO) => {
  383. const _imgIds = row?.ossId || ids.value;
  384. await proxy?.$modal.confirm('是否确认删除图片编号为"' + _imgIds + '"的数据项?').finally(() => loading.value = false);
  385. await delImg(_imgIds);
  386. proxy?.$modal.msgSuccess("删除成功");
  387. await getList();
  388. }
  389. /** 导出按钮操作 */
  390. const handleExport = () => {
  391. proxy?.download('system/img/export', {
  392. ...queryParams.value
  393. }, `img_${new Date().getTime()}.xlsx`)
  394. }
  395. onMounted(() => {
  396. getList();
  397. getPositionList();
  398. showPosition.value = false;
  399. });
  400. // 对象转成指定字符串分隔
  401. const listToString = (list: any[], separator?: string) => {
  402. let strs = "";
  403. separator = separator || ",";
  404. list.forEach(item => {
  405. if (item.ossId) {
  406. strs += item.ossId + separator;
  407. }
  408. })
  409. return strs != "" ? strs.substring(0, strs.length - 1) : "";
  410. }
  411. const getPositionList = async () => {
  412. const res = await listPosition();
  413. positionList.value = res.data;
  414. }
  415. const handleChange = (val: string) => {
  416. console.info(val === '0', 'val === 0');
  417. if(val === '0') {
  418. showPosition.value = true;
  419. }else{
  420. showPosition.value = false;
  421. }
  422. };
  423. </script>