home.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <template>
  2. <div class="contentBox">
  3. <el-card class="box-card-left">
  4. <div slot="header">
  5. <span>菜单列表</span>
  6. </div>
  7. <deepTree @nodeClick="treeClick" class="deepTree" :data="treeList"></deepTree>
  8. </el-card>
  9. <el-card class="box-card">
  10. <div slot="header" class="clearfix">
  11. <span>文章管理</span>
  12. <el-button style="float: right; padding: 3px 0" type="text" @click="addcontents" icon="el-icon-plus">添加文章</el-button>
  13. </div>
  14. <div class="main">
  15. <filterList ref="filterList" :operation="operation" :tableData="contents" :filed="filed" @edit="filtereEdit" @delete="filterDelete" @query="filterQuery" :total="Total">
  16. <template v-slot:search="{ item, formInline }">
  17. <el-select v-model="formInline[item.name]" placeholder="请选择菜单" v-if="item.name == 'bind'">
  18. <el-option v-for="item in menus" :key="item.code" :label="item.name" :value="item.code"></el-option>
  19. </el-select>
  20. </template>
  21. </filterList>
  22. </div>
  23. </el-card>
  24. <dialogAndDrawer :width="'35%'" :title="title" :visibleSync="visibleSync" v-if="visibleSync" @close="visibleSync = false, lookUser = false">
  25. <template v-slot:windowMain>
  26. <formData v-if="!lookUser" ref="formData" :filed="formfiled" :data="formdata" :rules="formrules" @save="formSave">
  27. <template v-slot:formItem="{ item, formdata }">
  28. <el-switch v-if="item.name == 'istop'" v-model="formdata[item.name]" active-color="#13ce66" inactive-color="#ff4949" :active-value="true" :inactive-value="false"></el-switch>
  29. <!-- 缩略图 -->
  30. <el-upload
  31. v-if="item.name == 'thumbnail'"
  32. class="avatar-uploader"
  33. action="/api/files/avatar/upload"
  34. :show-file-list="false"
  35. :headers="myHeaders"
  36. :on-success="handleAvatarSuccess"
  37. :before-upload="beforeAvatarUpload">
  38. <img v-if="formdata[item.name] && formdata[item.name] !== ''" :src="formdata[item.name]" class="avatar">
  39. <i v-else class="el-icon-plus avatar-uploader-icon"></i>
  40. </el-upload>
  41. <!-- 富文本 -->
  42. <editoritem v-if="item.name == 'content'" @change="editChage" :value="formdata[item.name]"></editoritem>
  43. <!-- 附件上传 -->
  44. <el-upload
  45. v-if="item.name == 'annex'"
  46. :headers="myHeaders"
  47. class="upload-demo"
  48. action="/api/files/annex/upload"
  49. :on-success="handleAnnexSuccess"
  50. :on-remove="handleRemove"
  51. :file-list="fileList">
  52. <el-button size="small" type="primary">附件上传</el-button>
  53. </el-upload>
  54. </template>
  55. </formData>
  56. <filterList v-else ref="filterList" :tableData="userList" :filter="false" :readOnly="true" :filed="userfiled" @query="userfilterQuery" :total="userTotal"></filterList>
  57. </template>
  58. </dialogAndDrawer>
  59. </div>
  60. </template>
  61. <script>
  62. import filterList from '@components/filterList/index.vue';
  63. import dialogAndDrawer from '@components/dialogAndDrawer.vue';
  64. import formData from '@components/formData/index.vue';
  65. import editoritem from '@components/editoritem.vue';
  66. import deepTree from '@components/deepTree.vue';
  67. import { mapState, mapActions } from 'vuex';
  68. const token = sessionStorage.getItem('token');
  69. export default {
  70. components: {
  71. filterList,
  72. dialogAndDrawer,
  73. formData,
  74. editoritem,
  75. deepTree
  76. },
  77. data() {
  78. return {
  79. types: null,
  80. info: {},
  81. lookUser: false,
  82. fileList: [],
  83. myHeaders: { Authorization: token },
  84. title: '',
  85. visibleSync: false,
  86. filed: [
  87. { name: 'title', label: '标题', filter: true },
  88. // { name: 'bind', label: '绑定栏目', formater: 'slot', filter: true },
  89. { name: 'visit', label: '访问量' }
  90. ],
  91. formdata: {},
  92. userfiled: [
  93. { name: 'name', label: '名称' },
  94. { name: 'phone', label: '手机号' }
  95. ],
  96. formfiled: [
  97. { name: 'thumbnail', label: '缩略图', formater: 'slot' },
  98. { name: 'title', label: '标题' },
  99. { name: 'curtTitle', label: '副标题' },
  100. { name: 'describe', label: '描述' },
  101. { name: 'bind', label: '绑定菜单', formater: 'dict:menus' },
  102. { name: 'date', label: '时间', formater: 'date:datetime' },
  103. { name: 'istop', label: '置顶', formater: 'slot' },
  104. { name: 'content', label: '内容', formater: 'slot' },
  105. { name: 'annex', label: '附件', formater: 'slot' }
  106. ],
  107. formrules: {
  108. thumbnail: [
  109. { required: true, message: '请上传缩略图', trigger: 'chage' }
  110. ],
  111. title: [
  112. { required: true, message: '请输入标题', trigger: 'blur' }
  113. ],
  114. describe: [
  115. { required: true, message: '请输入描述', trigger: 'blur' }
  116. ],
  117. bind: [
  118. { required: true, message: '请绑定菜单', trigger: 'chage' }
  119. ],
  120. date: [
  121. { required: true, message: '请输入时间', trigger: 'blur' }
  122. ],
  123. content: [
  124. { required: true, message: '请输入内容', trigger: 'blur' }
  125. ]
  126. },
  127. operation: [
  128. { name: 'edit', label: '修改', icon: 'el-icon-edit' },
  129. { name: 'delete', label: '删除', icon: 'el-icon-delete' }
  130. ]
  131. };
  132. },
  133. computed: {
  134. ...mapState(['contentsList', 'Total', 'menusList', 'userList', 'userTotal', 'dict']),
  135. contents() {
  136. this.contentsList.map(p => {
  137. const findName = this.menusList.find(e => e.code == p.bind);
  138. if (findName) p.bind = findName?.name;
  139. return p;
  140. });
  141. return this.contentsList;
  142. },
  143. menus() {
  144. const menus = this.menusList.filter(e => e.parentCode !== 'null' && e.type == 1);
  145. return menus;
  146. },
  147. treeList() {
  148. const menusall = this.menusList.filter(e => (e.type !== 2 && e.type !== '2'));
  149. return this.$tree(menusall);
  150. }
  151. },
  152. async created() {
  153. await this.filterQuery();
  154. await this.menusQuery();
  155. },
  156. methods: {
  157. ...mapActions(['contentsQuery', 'contentsCreate', 'contentsUpdate', 'contentsDelete', 'contentsQuery', 'contentsFetch', 'menusQuery', 'userQuery']),
  158. // 添加
  159. addcontents () {
  160. this.formdata = {};
  161. this.title = '添加文章';
  162. this.visibleSync = true;
  163. if (this.types !== null) this.$set(this.formdata, 'bind', this.types);
  164. },
  165. // 修改
  166. async filtereEdit (e) {
  167. const res = await this.contentsFetch({ id: e._id });
  168. this.formdata = res.data;
  169. this.title = '修改文章';
  170. this.visibleSync = true;
  171. },
  172. // 删除
  173. async filterDelete (e) {
  174. const res = await this.contentsDelete({ id: e?._id });
  175. this.$resChange(res, '删除成功');
  176. this.filterQuery();
  177. },
  178. // 查询
  179. async filterQuery ({ filter = {}, paging = { page: 0, size: 10 } } = {}) {
  180. if (this.types !== null) filter.bind = this.types;
  181. await this.contentsQuery({ filter, paging });
  182. },
  183. // 表单保存
  184. async formSave (e) {
  185. if (e.isRevise && e?.isRevise == false) {
  186. this.$message.warning('未作修改');
  187. return;
  188. }
  189. this.$delete(e, 'isRevise');
  190. let res, msg;
  191. // 修改
  192. if (e._id) {
  193. delete e.svip;
  194. delete e.isShow;
  195. res = await this.contentsUpdate(e);
  196. msg = '文章修改成功';
  197. } else {
  198. res = await this.contentsCreate(e);
  199. msg = '文章修改成功';
  200. }
  201. this.$resChange(res, msg);
  202. const filter = { bind: this.types };
  203. this.filterQuery({ filter });
  204. this.visibleSync = false;
  205. this.$refs.filterList.resetPage(-1);
  206. },
  207. // 富文本改变
  208. editChage (e) {
  209. this.$refs.formData.setForm('content', e);
  210. },
  211. // 附件上传
  212. handleAnnexSuccess(res, file) {
  213. this.$refs.formData.setForm('annex', res.data.filePath);
  214. this.fileList.push({ name: res.data.name, url: res.data.filePath });
  215. },
  216. // 删除附件列表
  217. handleRemove(file, fileList) {
  218. this.$refs.formData.setForm('annex', null);
  219. delete this.fileList[0];
  220. },
  221. // 缩略图上传
  222. handleAvatarSuccess(res, file) {
  223. this.$refs.formData.setForm('thumbnail', res.data.filePath);
  224. },
  225. // 缩略图上传限制
  226. beforeAvatarUpload(file) {
  227. const isJPG = file.type === 'image/jpeg';
  228. const isLt2M = file.size / 1024 / 1024 < 2;
  229. if (!isJPG) {
  230. this.$message.error('上传图片只能是 JPG 格式!');
  231. }
  232. if (!isLt2M) {
  233. this.$message.error('上传图片大小不能超过 2MB!');
  234. }
  235. return isJPG && isLt2M;
  236. },
  237. async treeClick({ data, node }) {
  238. if (data.children) return;
  239. this.types = data.code;
  240. const filter = { bind: data.code };
  241. await this.filterQuery({ filter });
  242. this.$refs.filterList.resetPage(-1);
  243. }
  244. }
  245. };
  246. </script>
  247. <style lang="scss" scoped>
  248. .contentBox {
  249. width: 100%;
  250. height: 100%;
  251. display: flex;
  252. .box-card {
  253. width: 80%;
  254. height: 100%;
  255. .el-card__body {
  256. height: 100%;
  257. }
  258. }
  259. .box-card-left {
  260. width: 20%;
  261. height: 100%;
  262. ::v-deep .el-card__body {
  263. height: 88%;
  264. .deepTree {
  265. width: 100%;
  266. height: 100%;
  267. overflow-y: auto;
  268. }
  269. .deepTree::-webkit-scrollbar {
  270. width: 0px;
  271. height: 0px;
  272. }
  273. }
  274. }
  275. }
  276. .el-dialog {
  277. .avatar-uploader-icon {
  278. font-size: 28px;
  279. color: #8c939d;
  280. width: 120px;
  281. height: 120px;
  282. line-height: 120px;
  283. text-align: center;
  284. }
  285. .avatar {
  286. width: 120px;
  287. height: 120px;
  288. display: block;
  289. }
  290. }
  291. </style>
  292. <style>
  293. .el-upload {
  294. border: 1px dashed #d9d9d9;
  295. border-radius: 6px;
  296. cursor: pointer;
  297. position: relative;
  298. overflow: hidden;
  299. }
  300. .el-upload:hover {
  301. border-color: #409EFF;
  302. }
  303. </style>