list.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. <template>
  2. <div class="ele-body">
  3. <el-card shadow="never">
  4. <!-- 搜索表单 -->
  5. <el-form :model="table.where" label-width="90px" class="ele-form-search"
  6. @keyup.enter.native="$refs.table.reload()" @submit.native.prevent>
  7. <el-row :gutter="15">
  8. <el-col :md="8" :sm="12">
  9. <el-form-item label="关键词:">
  10. <el-input v-model="table.where.keyword" placeholder="请输入标题关键词" clearable />
  11. </el-form-item>
  12. </el-col>
  13. <el-col :md="6" :sm="12" v-if="!this.defaultType">
  14. <el-form-item label="类型:">
  15. <el-select v-model="table.where.type" placeholder="请选择类型" class="ele-fluid">
  16. <el-option label="全部" :value="0" />
  17. <el-option v-for="(item, k) in typeArr" :key="k" :label="item.name" :value="item.id" />
  18. </el-select>
  19. </el-form-item>
  20. </el-col>
  21. <el-col :md="6" :sm="12">
  22. <el-form-item label="状态:">
  23. <el-select v-model="table.where.status" placeholder="请选择" class="ele-fluid">
  24. <el-option label="全部" :value="0" />
  25. <el-option label="发布" :value="1" />
  26. <el-option label="未发布" :value="2" />
  27. </el-select>
  28. </el-form-item>
  29. </el-col>
  30. <el-col :md="6" :sm="12">
  31. <div class="ele-form-actions">
  32. <el-button type="primary" @click="$refs.table.reload()" icon="el-icon-search" class="ele-btn-icon">查询
  33. </el-button>
  34. <el-button @click="reloadTable()">
  35. 重置
  36. </el-button>
  37. <el-button v-if="permission.includes(permissionMap['add'])" type="primary" icon="el-icon-plus"
  38. size="small" @click="openCreate">
  39. 创建
  40. </el-button>
  41. <!-- 导入按钮,仅在 defaultType 为 9 时可见 -->
  42. <el-button v-if="defaultType == 9 && permission.includes(permissionMap['add'])" type="success"
  43. icon="el-icon-upload2" size="small" @click="openImportDialog">
  44. 导入
  45. </el-button>
  46. </div>
  47. </el-col>
  48. </el-row>
  49. </el-form>
  50. <ele-data-table :key="tableKey" ref="table" :config="table" :choose.sync="choose" height="calc(100vh - 315px)"
  51. highlight-current-row>
  52. <template>
  53. <el-table-column type="selection" width="45" align="center" fixed="left" />
  54. <el-table-column prop="id" label="ID" width="60" align="center" fixed="left" show-overflow-tooltip />
  55. <el-table-column prop="title" label="标题" show-overflow-tooltip min-width="200" />
  56. <el-table-column v-if="defaultType == 1" prop="author" label="作者" show-overflow-tooltip min-width="100">
  57. <template slot-scope="{row}">
  58. <span>{{ row.author }}</span>
  59. </template>
  60. </el-table-column>
  61. <el-table-column v-if="defaultType != 1" prop="type_name" label="类型" show-overflow-tooltip min-width="100">
  62. <template slot-scope="{row}">
  63. <span class="ele-text-primary">{{ getTypeName(row.type) }}</span>
  64. </template>
  65. </el-table-column>
  66. <el-table-column prop="status" label="发布" :resizable="false" min-width="120">
  67. <template slot-scope="{row}">
  68. <el-switch v-model="row.status" @change="handleStatusChange(row)" :active-value="1" :inactive-value="2" />
  69. </template>
  70. </el-table-column>
  71. <el-table-column label="操作" width="130px" align="center" :resizable="false" fixed="right">
  72. <template slot-scope="{row}">
  73. <el-link @click="edit(row)" icon="el-icon-edit" type="primary" :underline="false"
  74. v-if="permission.includes(permissionMap['edit'])">修改</el-link>
  75. </template>
  76. </el-table-column>
  77. </template>
  78. </ele-data-table>
  79. </el-card>
  80. <article-form :defaultType="this.defaultType" :visible.sync="showArticleForm" :defaultData="editArticle"
  81. @success="reloadTable" />
  82. <excel-import-dialog :visible.sync="importVisible" @imported="handleImportSuccess" />
  83. </div>
  84. </template>
  85. <script>
  86. import ExcelImportDialog from './ExcelImportDialog.vue'; // 引入导入弹窗组件
  87. import ArticleForm from './ArticleForm.vue'
  88. import { mapGetters } from "vuex";
  89. export default {
  90. props: {
  91. defaultType: { type: [Number, String], default: null },
  92. permissionMap: { type: Object, default: null },
  93. },
  94. name: "Sysarticle",
  95. components: {
  96. ArticleForm,
  97. ExcelImportDialog,
  98. },
  99. data() {
  100. return {
  101. tableKey: 0,
  102. table: { url: '/article/index', where: { status: 0, type: 0, show_type: 1, type: null } }, // 表格配置
  103. choose: [], // 表格选中数据
  104. showEdit: false, // 是否显示表单弹窗
  105. editForm: { status: 1, type: 1 }, // 表单数据
  106. // 类型写死在前端
  107. typeArr: [
  108. { id: 1, name: '文章资讯' },
  109. { id: 2, name: '注册协议' },
  110. { id: 3, name: '隐私政策' },
  111. { id: 4, name: 'VIP购买协议' },
  112. { id: 9, name: '智能问答' },
  113. ],
  114. editRules: { // 表单验证规则
  115. title: [
  116. { required: true, message: '请输入名称', trigger: 'blur' }
  117. ],
  118. type: [
  119. { required: true, message: '请选择分类', trigger: 'blur' }
  120. ],
  121. },
  122. // 分类
  123. cates: [],
  124. loading: false,
  125. // 自定义文件上传(这里使用把选择的文件转成blob演示)
  126. file_picker_callback: (callback, value, meta) => {
  127. let input = document.createElement('input');
  128. input.setAttribute('type', 'file');
  129. // 设定文件可选类型
  130. if (meta.filetype === 'image') {
  131. input.setAttribute('accept', 'image/*');
  132. } else if (meta.filetype === 'media') {
  133. input.setAttribute('accept', 'video/*');
  134. }
  135. input.onchange = () => {
  136. let file = input.files[0];
  137. let reader = new FileReader();
  138. reader.onload = (e) => {
  139. let blob = new Blob([e.target.result], { type: file.type });
  140. callback(URL.createObjectURL(blob));
  141. };
  142. reader.readAsArrayBuffer(file);
  143. }
  144. input.click();
  145. },
  146. showArticleForm: false,
  147. editArticle: { status: 1, type: 1 },
  148. importVisible: false, // 控制导入弹窗显示
  149. }
  150. },
  151. mounted() {
  152. // 如果父组件传入 defaultType,则直接作为表格过滤条件并刷新表格
  153. if (this.defaultType) {
  154. // 创建新对象,保证引用变化
  155. this.table = {
  156. ...this.table,
  157. where: { ...this.table.where, type: Number(this.defaultType) }
  158. }
  159. // 通过设置 key 强制刷新整个表格组件
  160. this.$nextTick(() => {
  161. this.$refs.table.reload()
  162. this.tableKey = Date.now() // 新增 data 属性 tableKey
  163. })
  164. }
  165. },
  166. computed: {
  167. ...mapGetters(["permission"]),
  168. editContent() {
  169. return {
  170. menubar: false,
  171. file_picker_callback: this.file_picker_callback,
  172. skin_url: this.$store.state.theme.theme === 'dark' ? '/tinymce/skins/ui/oxide-dark' : '/tinymce/skins/ui/oxide',
  173. content_css: this.$store.state.theme.theme === 'dark' ? '/tinymce/skins/content/dark/content.css' : '/tinymce/skins/content/default/content.css'
  174. };
  175. }
  176. },
  177. methods: {
  178. getTypeName(typeId) {
  179. const item = this.typeArr.find(t => t.id === typeId)
  180. return item ? item.name : '未知类型'
  181. },
  182. openCreate() {
  183. this.editArticle = { status: 1, type: this.defaultType ? Number(this.defaultType) : 1 }
  184. this.showArticleForm = true
  185. },
  186. openEdit(article) {
  187. this.editArticle = Object.assign({}, article)
  188. this.showArticleForm = true
  189. },
  190. reloadTable() {
  191. this.$refs.table.reload()
  192. },
  193. edit(row) {
  194. this.editArticle = Object.assign({}, row)
  195. this.showArticleForm = true
  196. },
  197. /* 保存编辑 */
  198. save() {
  199. this.$refs['editForm'].validate((valid) => {
  200. if (valid) {
  201. const loading = this.$loading({ lock: true });
  202. this.$http.post('/article/edit', this.editForm).then(res => {
  203. loading.close();
  204. if (res.data.code === 0) {
  205. this.showEdit = false;
  206. this.$message({ type: 'success', message: res.data.msg });
  207. this.$refs.table.reload();
  208. } else {
  209. this.$message.error(res.data.msg);
  210. }
  211. }).catch(e => {
  212. loading.close();
  213. this.$message.error(e.message);
  214. });
  215. } else {
  216. return false;
  217. }
  218. });
  219. },
  220. /* 删除 */
  221. remove(row) {
  222. if (!row) { // 批量删除
  223. if (this.choose.length === 0) return this.$message.error('请至少选择一条数据');
  224. let ids = this.choose.map(d => d.id);
  225. this.$confirm('确定要删除选中的记录吗?', '提示', { type: 'warning' }).then(() => {
  226. const loading = this.$loading({ lock: true });
  227. this.$http.post('/article/delete', { id: ids }).then(res => {
  228. loading.close();
  229. if (res.data.code === 0) {
  230. this.$message({ type: 'success', message: res.data.msg });
  231. this.$refs.table.reload();
  232. } else {
  233. this.$message.error(res.data.msg);
  234. }
  235. }).catch(e => {
  236. loading.close();
  237. this.$message.error(e.message);
  238. });
  239. }).catch(() => 0);
  240. } else { // 单个删除
  241. const loading = this.$loading({ lock: true });
  242. this.$http.post('/article/delete', { id: row.id }).then(res => {
  243. loading.close();
  244. if (res.data.code === 0) {
  245. this.$message({ type: 'success', message: res.data.msg });
  246. this.$refs.table.reload();
  247. } else {
  248. this.$message.error(res.data.msg);
  249. }
  250. }).catch(e => {
  251. loading.close();
  252. this.$message.error(e.message);
  253. });
  254. }
  255. },
  256. /* 处理状态切换 */
  257. handleStatusChange(row) {
  258. this.editStatus(row);
  259. },
  260. /* 更改状态 */
  261. editStatus(row) {
  262. this.$message.closeAll()
  263. const loading = this.$loading({ lock: true });
  264. let params = { id: row.id, status: row.status };
  265. this.$http.post('/article/status', params).then(res => {
  266. loading.close();
  267. if (res.data.code === 0) {
  268. this.$message({ type: 'success', message: res.data.msg });
  269. } else {
  270. // 失败时恢复原状态
  271. row.status = row.status === 1 ? 2 : 1;
  272. this.$message.error(res.data.msg);
  273. }
  274. }).catch(e => {
  275. loading.close();
  276. // 失败时恢复原状态
  277. row.status = row.status === 1 ? 2 : 1;
  278. this.$message.error(e.message);
  279. });
  280. },
  281. openImportDialog() {
  282. this.importVisible = true; // 显示导入弹窗
  283. },
  284. handleImportSuccess() {
  285. // 处理导入成功后的操作
  286. this.reloadTable()
  287. this.$message.success('导入操作完成');
  288. },
  289. }
  290. }
  291. </script>
  292. <style scoped>
  293. .ele-block>>>.el-upload,
  294. .ele-block>>>.el-upload-dragger {
  295. width: 100%;
  296. }
  297. </style>