Эх сурвалжийг харах

feat: 🎸 显示更多组建开发中

chenkl 4 жил өмнө
parent
commit
fa9f24d5da

+ 2 - 2
package.json

@@ -19,15 +19,15 @@
     "clipboard": "^2.0.6",
     "core-js": "^3.6.5",
     "echarts": "^4.9.0",
-    "element-plus": "1.0.1-beta.11",
+    "element-plus": "1.0.1-beta.14",
     "highlight.js": "^10.4.0",
     "lodash-es": "^4.17.15",
+    "mitt": "^2.1.0",
     "mockjs": "^1.1.0",
     "normalize.css": "^8.0.1",
     "nprogress": "^0.2.0",
     "path-to-regexp": "^6.2.0",
     "qs": "^6.9.4",
-    "resize-observer-polyfill": "^1.5.1",
     "screenfull": "^5.0.2",
     "vditor": "^3.7.0",
     "vue": "3.0.4",

+ 84 - 0
src/components/More/index.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="more__item clearfix">
+    <p class="more__item--text">{{ content }}</p>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, PropType, computed, unref, onMounted, nextTick, ref } from 'vue'
+export default defineComponent({
+  name: 'More',
+  props: {
+    // 内容
+    content: {
+      type: String as PropType<string>,
+      default: ''
+    },
+    // 默认展示几行
+    lineClamp: {
+      type: Number as PropType<number>,
+      default: 0
+    },
+    // 宽度
+    width: {
+      type: String as PropType<string>,
+      default: '300px'
+    },
+    // style
+    style: {
+      type: Object as PropType<object>,
+      default: () => {
+        return {
+          width: '300px',
+          float: 'left'
+        }
+      }
+    }
+  },
+  setup(props) {
+    const styleObj = computed(() => {
+
+    })
+  }
+})
+</script>
+
+<style lang="less" scoped>
+.more__item {
+  width: 528px;
+  height: 122px;
+  float: left;
+  .more__item--text {
+    width: 476px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 3;
+    -webkit-box-orient: vertical;
+    font-size: 14px;
+    color: #545c63;
+    line-height: 28px;
+    transition: all .1s;
+    text-align: left;
+    &:hover {
+      background: #fff;
+      height: auto;
+      position: relative;
+      z-index: 5;
+      border-radius: 8px;
+      box-shadow: 0 8px 16px 0 rgba(7,17,27,.2);
+      -webkit-line-clamp: inherit;
+       padding: 10px;
+      margin-top: -10px;
+      margin-left: -10px;
+    }
+  }
+}
+.clearfix:after {
+  content: "";
+  display: block;
+  height: 0;
+  clear: both;
+  visibility: hidden;
+}
+</style>

+ 0 - 20
src/components/Setting/index.vue

@@ -35,14 +35,6 @@
       <div class="setting__title">顶部菜单主题</div> -->
 
       <!-- <div class="setting__title">界面功能</div> -->
-      <!-- <div class="setting__item">
-        <span>固定顶部操作栏</span>
-        <el-switch v-model="fixedNavbar" @change="setFixedNavbar" />
-      </div>
-      <div class="setting__item">
-        <span>固定标签页</span>
-        <el-switch v-model="fixedTags" @change="setFixedTags" />
-      </div> -->
 
       <div class="setting__title">界面显示</div>
       <div class="setting__item">
@@ -116,16 +108,6 @@ export default defineComponent({
       appStore.SetCollapsed(false)
     }
 
-    // const fixedNavbar = ref<boolean>(appStore.fixedNavbar)
-    // function setFixedNavbar(fixedNavbar: boolean) {
-    //   appStore.SetFixedNavbar(fixedNavbar)
-    // }
-
-    // const fixedTags = ref<boolean>(appStore.fixedTags)
-    // function setFixedTags(fixedTags: boolean) {
-    //   appStore.SetFixedTags(fixedTags)
-    // }
-
     const fixedHeader = ref<boolean>(appStore.fixedHeader)
     function setFixedHeader(fixedHeader: boolean) {
       appStore.SetFixedHeader(fixedHeader)
@@ -179,8 +161,6 @@ export default defineComponent({
     return {
       drawer, toggleClick,
       layout, setLayout,
-      // fixedNavbar, setFixedNavbar,
-      // fixedTags, setFixedTags,
       fixedHeader, setFixedHeader,
       navbar, setNavbar,
       hamburger, setHamburger,

+ 1 - 1
src/pages/index/layout/components/AppMain.vue

@@ -26,7 +26,7 @@ export default defineComponent({
 
 <style lang="less" scoped>
 .app-main {
-  overflow: hidden;
+  // overflow: hidden;
   padding: 20px;
 }
 </style>

+ 48 - 4
src/pages/index/router/index.ts

@@ -24,6 +24,8 @@ const Layout = () => import('../layout/index.vue')
     affix: true               如果设置为true,则会一直固定在tag项中(默认 false)
     noTagsView: true           如果设置为true,则不会出现在tag中(默认 false)
     activeMenu: '/dashboard'  显示高亮的路由路径
+    followAuth: '/dashboard'  跟随哪个路由进行权限过滤
+    showMainRoute: true       设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)
   }
 **/
 
@@ -178,6 +180,14 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
         meta: {
           title: '弹窗'
         }
+      },
+      {
+        path: 'more',
+        component: () => import('_p/index/views/components-demo/more/index.vue'),
+        name: 'MoreDemo',
+        meta: {
+          title: '显示更多'
+        }
       }
     ]
   },
@@ -499,11 +509,45 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
     },
     children: [
       {
-        path: 'example',
-        component: () => import('_p/index/views/example-demo/example/index.vue'),
-        name: 'Example',
+        path: 'example-dialog',
+        component: () => import('_p/index/views/example-demo/example-dialog/index.vue'),
+        name: 'ExampleDialog',
+        meta: {
+          title: '列表综合实例-弹窗'
+        }
+      },
+      {
+        path: 'example-page',
+        component: () => import('_p/index/views/example-demo/example-page/index.vue'),
+        name: 'ExamplePage',
+        meta: {
+          title: '列表综合实例-页面'
+        }
+      },
+      {
+        path: 'example-add',
+        component: () => import('_p/index/views/example-demo/example-page/example-add.vue'),
+        name: 'ExampleAdd',
         meta: {
-          title: '列表综合实例'
+          title: '列表综合实例-新增',
+          noTagsView: true,
+          noCache: true,
+          hidden: true,
+          showMainRoute: true,
+          activeMenu: '/example-demo/example-page'
+        }
+      },
+      {
+        path: 'example-edit',
+        component: () => import('_p/index/views/example-demo/example-page/example-edit.vue'),
+        name: 'ExampleEdit',
+        meta: {
+          title: '列表综合实例-编辑',
+          noTagsView: true,
+          noCache: true,
+          hidden: true,
+          showMainRoute: true,
+          activeMenu: '/example-demo/example-page'
         }
       }
     ]

+ 2 - 0
src/pages/index/router/types.d.ts

@@ -10,6 +10,8 @@ export interface RouteMeta {
   activeMenu?: string
   parent?: string
   noTagsView?: boolean
+  followAuth?: string
+  showMainRoute?: boolean
 }
 
 export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {

+ 0 - 20
src/pages/index/store/modules/app.ts

@@ -7,8 +7,6 @@ export interface AppState {
   showLogo: Boolean
   showNavbar: Boolean
   fixedHeader: Boolean
-  // fixedTags: Boolean
-  // fixedNavbar: Boolean
   layout: String
   showBreadcrumb: Boolean
   showHamburger: Boolean
@@ -25,8 +23,6 @@ class App extends VuexModule implements AppState {
   public showLogo = true // 是否显示logo
   public showTags = true // 是否显示标签栏
   public showNavbar = true // 是否显示navbar
-  // public fixedTags = true // 是否固定标签栏
-  // public fixedNavbar = true // 是否固定navbar
   public fixedHeader = true // 是否固定header
   public layout = 'Classic' // layout布局
   public showBreadcrumb = true // 是否显示面包屑
@@ -53,14 +49,6 @@ class App extends VuexModule implements AppState {
   private SET_NAVBAR(showNavbar: boolean): void {
     this.showNavbar = showNavbar
   }
-  // @Mutation
-  // private SET_FIXEDTAGS(fixedTags: boolean): void {
-  //   this.fixedTags = fixedTags
-  // }
-  // @Mutation
-  // private SET_FIXEDNAVBAR(fixedNavbar: boolean): void {
-  //   this.fixedNavbar = fixedNavbar
-  // }
   @Mutation
   private SET_FIXEDHEADER(fixedHeader: boolean): void {
     this.fixedHeader = fixedHeader
@@ -114,14 +102,6 @@ class App extends VuexModule implements AppState {
   public SetFixedHeader(fixedHeader: boolean): void {
     this.SET_FIXEDHEADER(fixedHeader)
   }
-  // @Action
-  // public SetFixedTags(fixedTags: boolean): void {
-  //   this.SET_FIXEDTAGS(fixedTags)
-  // }
-  // @Action
-  // public SetFixedNavbar(fixedNavbar: boolean): void {
-  //   this.SET_FIXEDNAVBAR(fixedNavbar)
-  // }
   @Action
   public SetLayout(layout: 'Classic' | 'LeftTop' | 'Top' | 'Test'): void {
     this.SET_LAYOUT(layout)

+ 1 - 33
src/pages/index/store/modules/permission.ts

@@ -51,45 +51,13 @@ class Permission extends VuexModule implements PermissionState {
   }
 }
 
-// // 二级以上的菜单降级成二级菜单
-// function formatRouter(routes: AppRouteRecordRaw[], basePath = '/', list: AppRouteRecordRaw[] = [], parent: AppRouteRecordRaw) {
-//   routes.map((item: AppRouteRecordRaw) => {
-//     item.path = path.resolve(basePath, item.path)
-//     const meta: RouteMeta = item.meta || {}
-//     if (!meta.parent && parent) {
-//       meta.parent = parent.path
-//       item.meta = meta
-//     }
-//     if (item.redirect && item.redirect !== 'noredirect') {
-//       item.redirect = path.resolve(basePath, item.redirect as any)
-//     }
-//     if (item.children && item.children.length > 0) {
-//       const arr: AppRouteRecordRaw[] = formatRouter(item.children, item.path, list, item)
-//       delete item.children
-//       list.concat(arr)
-//     }
-//     list.push(item)
-//   })
-//   return list
-// }
-
-// // 菜单降级
-// function getFlatRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
-//   return routes.map((child: AppRouteRecordRaw) => {
-//     if (child.children && child.children.length > 0) {
-//       child.children = formatRouter(child.children, child.path, [], child)
-//     }
-//     return child
-//   })
-// }
-
 // 路由过滤,主要用于权限控制
 function generateRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
   const res: AppRouteRecordRaw[] = []
 
   for (const route of routes) {
     // skip some route
-    if (route.meta && route.meta.hidden) {
+    if (route.meta && route.meta.hidden && !route.meta.showMainRoute) {
       continue
     }
 

+ 46 - 0
src/pages/index/views/components-demo/more/index.vue

@@ -0,0 +1,46 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="显示更多组件。"
+      type="info"
+      style="margin-bottom: 20px;"
+    />
+
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="默认显示一行,超出隐藏。"
+      type="info"
+      style="margin-bottom: 20px;margin-top: 20px;"
+    />
+    <div>
+      <More :line-clamp="3" content="2020版是根据(前端)全栈工程师岗位要求,结合时下技术热点及未来前端术发展趋势全新制作的课程,让你实现从零基础到具备全栈能力的就业水平。相比之前的版本,我们有几大重要内容:1. 两大核心企业项目贯穿整个体系,实现项目闭环开发,亲历从0到1的企业级项目。项目之间强关联,还原企业开发模式,渐进式实现,过程更顺滑。第一个旅游网项目,还原企业迭代开发,同一个项目从PC端演变到移动webapp,掌握跨端开发能力。 第二个电商全栈项目,用Vue.js 实现前端,node.js+koa2+MongoDB实现后端,打造6大核心业务,15+精美网页,12+真实数据接口,并打通前后端数据联调。2. 项目驱动式教学,案例融入课程讲解中,让学习不枯燥。多领域案例帮你开拓眼界和经验。3. 不用担心版本问题,课程截止上线使用最新版本。比如Vue3.0等,让大家学到新技术。4. 技术覆盖更实用更全面,从0基础到全栈能力循序渐进的培养,慕课网宗旨只学有用的。帮你打通跨端和全栈技能。5. 慕课网网红讲师亲授,讲解更细致,阶梯式习题,整门课程代码量将达到6万行,相当于1~2年工作经验所敲代码。" />
+    </div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="默认显示3行,超出隐藏。"
+      type="info"
+      style="margin-bottom: 20px;margin-top: 20px;"
+    />
+    <div>
+      <More :line-clamp="3" content="2020版是根据(前端)全栈工程师岗位要求,结合时下技术热点及未来前端术发展趋势全新制作的课程,让你实现从零基础到具备全栈能力的就业水平。相比之前的版本,我们有几大重要内容:1. 两大核心企业项目贯穿整个体系,实现项目闭环开发,亲历从0到1的企业级项目。项目之间强关联,还原企业开发模式,渐进式实现,过程更顺滑。第一个旅游网项目,还原企业迭代开发,同一个项目从PC端演变到移动webapp,掌握跨端开发能力。 第二个电商全栈项目,用Vue.js 实现前端,node.js+koa2+MongoDB实现后端,打造6大核心业务,15+精美网页,12+真实数据接口,并打通前后端数据联调。2. 项目驱动式教学,案例融入课程讲解中,让学习不枯燥。多领域案例帮你开拓眼界和经验。3. 不用担心版本问题,课程截止上线使用最新版本。比如Vue3.0等,让大家学到新技术。4. 技术覆盖更实用更全面,从0基础到全栈能力循序渐进的培养,慕课网宗旨只学有用的。帮你打通跨端和全栈技能。5. 慕课网网红讲师亲授,讲解更细致,阶梯式习题,整门课程代码量将达到6万行,相当于1~2年工作经验所敲代码。" />
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue'
+import More from '_c/More/index.vue'
+export default defineComponent({
+  // name: 'MoreDemo',
+  components: {
+    More
+  }
+})
+</script>
+
+<style>
+</style>

+ 0 - 0
src/pages/index/views/example-demo/example/api.ts → src/pages/index/views/example-demo/example-dialog/api.ts


+ 0 - 0
src/pages/index/views/example-demo/example/components/IfnoWrite.vue → src/pages/index/views/example-demo/example-dialog/components/IfnoWrite.vue


+ 0 - 0
src/pages/index/views/example-demo/example/components/types.ts → src/pages/index/views/example-demo/example-dialog/components/types.ts


+ 1 - 1
src/pages/index/views/example-demo/example/index.vue → src/pages/index/views/example-demo/example-dialog/index.vue

@@ -112,7 +112,7 @@ const columns = [
 ]
 
 export default defineComponent({
-  // name: 'Example',
+  // name: 'ExampleDialog',
   components: {
     IfnoWrite
   },

+ 22 - 0
src/pages/index/views/example-demo/example-page/api.ts

@@ -0,0 +1,22 @@
+import { fetch } from '_p/index/axios-config/axios'
+
+interface PropsData {
+  params?: any
+  data?: any
+}
+
+export const getExampleListApi = ({ params }: PropsData): any => {
+  return fetch({ url: '/example/list', method: 'get', params })
+}
+
+export const delsExampApi = ({ data }: PropsData): any => {
+  return fetch({ url: '/example/delete', method: 'post', data })
+}
+
+export const saveExampApi = ({ data }: PropsData): any => {
+  return fetch({ url: '/example/save', method: 'post', data })
+}
+
+export const getExampDetApi = ({ params }: PropsData): any => {
+  return fetch({ url: '/example/detail', method: 'get', params })
+}

+ 188 - 0
src/pages/index/views/example-demo/example-page/components/IfnoWrite.vue

@@ -0,0 +1,188 @@
+<template>
+  <div>
+    <el-form
+      ref="formRef"
+      :model="form"
+      :rules="rules"
+      label-width="100px"
+    >
+      <el-row>
+        <el-col :span="24">
+          <el-form-item prop="title" label="标题">
+            <el-input v-model="form.title" placeholder="请输入标题" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item prop="author" label="作者">
+            <el-input v-model="form.author" placeholder="请输入作者" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item prop="display_time" label="创建时间">
+            <el-date-picker
+              v-model="form.display_time"
+              type="datetime"
+              placeholder="请选择创建时间"
+              style="width: 100%;"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item prop="importance" label="重要性">
+            <el-select v-model="form.importance" placeholder="请选择重要性" style="width: 100%;">
+              <el-option label="重要" value="3" />
+              <el-option label="良好" value="2" />
+              <el-option label="一般" value="1" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item prop="pageviews" label="阅读数">
+            <el-input-number
+              v-model="form.pageviews"
+              :min="0"
+              :max="99999999"
+              style="width: 100%;"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item prop="content" label="内容">
+            <editor ref="editorRef" :value="form.content" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <div class="dialong__button--wrap">
+      <el-button @click="close">取消</el-button>
+      <el-button :loading="subLoading" type="primary" @click="setListData">保存</el-button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, reactive, ref, unref, PropType } from 'vue'
+import { useRouter } from 'vue-router'
+import Editor from '_c/Editor/index.vue'
+import { Message } from '_c/Message'
+import { formatTime } from '@/utils'
+import { InfoWriteParams, InfoWriteRules } from './types'
+import { saveExampApi, getExampDetApi } from '../api'
+
+const requiredRule = {
+  required: true,
+  message: '该项为必填项'
+}
+
+export default defineComponent({
+  name: 'IfnoWrite',
+  components: {
+    Editor
+  },
+  props: {
+    id: {
+      type: String as PropType<string>,
+      default: () => ''
+    }
+  },
+  emits: ['close', 'success'],
+  setup(props, { emit }) {
+    const { push } = useRouter()
+
+    const formRef = ref<HTMLElement | null>(null)
+    const editorRef = ref<HTMLElement | null>(null)
+    const subLoading = ref<boolean>(false)
+
+    const form = reactive<InfoWriteParams>({
+      id: '', // id
+      author: '', // 作者
+      title: '', // 标题
+      content: '', // 内容
+      importance: '', // 重要性
+      display_time: '', // 创建时间
+      pageviews: 0 // 阅读数
+    })
+
+    const rules = reactive<InfoWriteRules>({
+      title: [requiredRule],
+      author: [requiredRule],
+      content: [requiredRule],
+      importance: [requiredRule],
+      display_time: [requiredRule],
+      pageviews: [requiredRule]
+    })
+
+    async function getDet() {
+      if (props.id) {
+        const id = props.id
+        try {
+          const res = await getExampDetApi({
+            params: {
+              id: id
+            }
+          })
+          if (res.code === '0000') {
+            for (const key in form) {
+              if (key === 'importance') {
+                form[key] = res.data[key].toString()
+              } else {
+                form[key] = res.data[key]
+              }
+            }
+          }
+        } catch (e) {
+          console.log(e)
+        }
+      }
+    }
+    getDet()
+
+    // 新增或者编辑
+    function setListData() {
+      const formRefWrap = unref(formRef as any)
+      const editorRefWrap = unref(editorRef as any)
+      form.content = editorRefWrap.getHtml()
+      try {
+        subLoading.value = true
+        formRefWrap.validate(async(valid: boolean) => {
+          if (valid) {
+            const formData = unref(form)
+            formData.display_time = formatTime(formData.display_time, 'yyyy-MM-dd HH:mm:ss')
+            const res = await saveExampApi({
+              data: formData
+            })
+            if (res.code === '0000') {
+              Message.success(form.id ? '编辑成功' : '新增成功')
+              emit('success', form.id ? 'edit' : 'add')
+            }
+          } else {
+            console.log('error submit!!')
+            return false
+          }
+        })
+      } catch (err) {
+        console.log(err)
+      } finally {
+        subLoading.value = false
+      }
+    }
+
+    function close() {
+      push('/example-demo/example-page')
+    }
+
+    return {
+      formRef, editorRef,
+      subLoading,
+      form,
+      rules,
+      setListData,
+      close,
+      getDet
+    }
+  }
+})
+</script>
+
+<style>
+</style>

+ 18 - 0
src/pages/index/views/example-demo/example-page/components/types.ts

@@ -0,0 +1,18 @@
+export interface InfoWriteParams {
+  title: string
+  id?: string
+  author: string
+  content: string
+  importance: string
+  display_time: string
+  pageviews: number
+}
+
+export interface InfoWriteRules {
+  title?: any[]
+  author?: any[]
+  content?: any[]
+  importance?: any[]
+  display_time?: any[]
+  pageviews?: any[]
+}

+ 30 - 0
src/pages/index/views/example-demo/example-page/example-add.vue

@@ -0,0 +1,30 @@
+<template>
+  <ifno-write @success="success" />
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue'
+import IfnoWrite from './components/IfnoWrite.vue'
+import vueBus from '@/vue-bus'
+
+export default defineComponent({
+  // name: 'ExampleAdd',
+  components: {
+    IfnoWrite
+  },
+  setup() {
+    // 成功之后的回调
+    function success(type: string) {
+      // 由于使用的是页面跳转,所以只能通过vueBus去进行通信
+      vueBus.$emit('success', type)
+    }
+
+    return {
+      success
+    }
+  }
+})
+</script>
+
+<style>
+</style>

+ 34 - 0
src/pages/index/views/example-demo/example-page/example-edit.vue

@@ -0,0 +1,34 @@
+<template>
+  <ifno-write :id="id" @success="success" />
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue'
+import IfnoWrite from './components/IfnoWrite.vue'
+import vueBus from '@/vue-bus'
+import { useRoute } from 'vue-router'
+
+export default defineComponent({
+  // name: 'ExampleEdit',
+  components: {
+    IfnoWrite
+  },
+  setup() {
+    const { query } = useRoute()
+
+    // 成功之后的回调
+    function success(type: string) {
+      // 由于使用的是页面跳转,所以只能通过vueBus去进行通信
+      vueBus.$emit('success', type)
+    }
+
+    return {
+      success,
+      id: query.id
+    }
+  }
+})
+</script>
+
+<style>
+</style>

+ 229 - 0
src/pages/index/views/example-demo/example-page/index.vue

@@ -0,0 +1,229 @@
+<template>
+  <div>
+    <div class="search__example--wrap">
+      <com-search
+        :data="searchData"
+        @search-submit="searchSubmit"
+        @reset-submit="resetSubmit"
+      />
+    </div>
+
+    <div class="button__example--wrap">
+      <el-button type="primary" icon="el-icon-circle-plus-outline" @click="open(false)">新增</el-button>
+      <el-button
+        type="danger"
+        icon="el-icon-delete"
+        @click="dels"
+      >删除</el-button>
+    </div>
+
+    <com-table
+      v-loading="loading"
+      selection
+      :columns="columns"
+      :data="tableData"
+      :pagination="{
+        currentPage: defalutParams.pageIndex,
+        total: total,
+        onSizeChange: handleSizeChange,
+        onCurrentChange: handleCurrentChange
+      }"
+      @selection-change="handleSelectionChange"
+    >
+      <template #importance="scope">
+        <el-tag
+          :type="scope.row.importance === 3
+            ? 'success'
+            : (scope.row.importance === 2
+              ? 'warning'
+              : 'danger')"
+        >{{ scope.row.importance === 3
+          ? '重要'
+          : (scope.row.importance === 2
+            ? '良好'
+            : '一般') }}
+        </el-tag>
+      </template>
+      <template #action="scope">
+        <el-button type="primary" size="mini" @click="open(scope.row)">编辑</el-button>
+        <el-button type="danger" size="mini" @click="dels(scope.row)">删除</el-button>
+      </template>
+    </com-table>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, onMounted, onUnmounted } from 'vue'
+import { useRouter } from 'vue-router'
+import vueBus from '@/vue-bus'
+import { useExample } from '@/hooks/useExample'
+import { Message } from '_c/Message'
+
+import { getExampleListApi, delsExampApi } from './api'
+
+const searchData = [
+  {
+    label: '标题',
+    value: '',
+    itemType: 'input',
+    field: 'title',
+    placeholder: '请输入标题',
+    clearable: true
+  }
+]
+
+const columns = [
+  {
+    key: 'title',
+    label: '标题',
+    showOverflowTooltip: true
+  },
+  {
+    key: 'author',
+    label: '作者'
+  },
+  {
+    key: 'display_time',
+    label: '创建时间'
+  },
+  {
+    key: 'importance',
+    label: '重要性',
+    slots: {
+      default: 'importance'
+    }
+  },
+  {
+    key: 'pageviews',
+    label: '阅读数'
+  },
+  {
+    key: 'action',
+    label: '操作',
+    width: '150px',
+    slots: {
+      default: 'action'
+    }
+  }
+]
+
+export default defineComponent({
+  name: 'ExamplePage',
+  setup() {
+    const { push } = useRouter()
+
+    const {
+      defalutParams,
+      tableData,
+      loading,
+      total,
+      currentChange,
+      sizeChange,
+      handleSelectionChange,
+      selectionData,
+      delData
+    } = useExample()
+
+    // 请求数据
+    async function getExampleList(data?: any): Promise<void> {
+      try {
+        const res = await getExampleListApi({
+          params: Object.assign(defalutParams, data || {})
+        })
+        if (res.code === '0000') {
+          total.value = res.data.total
+          tableData.value = res.data.list
+        }
+      } finally {
+        loading.value = false
+      }
+    }
+
+    // 查询
+    function searchSubmit(data: any) {
+      // 该方法重置了一些默认参数
+      currentChange(1)
+      getExampleList(data)
+    }
+
+    // 重置
+    function resetSubmit(data: any) {
+      // 该方法重置了一些默认参数
+      currentChange(1)
+      getExampleList(data)
+    }
+
+    // 展示多少条
+    function handleSizeChange(val: number) {
+      // 该方法重置了一些默认参数
+      sizeChange(val)
+      getExampleList()
+    }
+
+    // 展示第几页
+    function handleCurrentChange(val: number) {
+      // 该方法重置了一些默认参数
+      currentChange(val)
+      getExampleList()
+    }
+
+    // 删除多选
+    function dels(item?: any) {
+      delData(async() => {
+        let ids: string[]
+        if (item.id) {
+          ids = [item.id]
+        } else {
+          ids = selectionData.value.map((v: any) => {
+            return v.id
+          })
+        }
+        const res = await delsExampApi({
+          data: { ids }
+        })
+        if (res.code === '0000') {
+          Message.success('删除成功!')
+          getExampleList()
+        }
+      }, { hiddenVerify: item.id })
+    }
+
+    // 打开新页面
+    function open(row: any) {
+      push(row ? `/example-demo/example-edit?id=${row.id}` : `/example-demo/example-add`)
+    }
+
+    getExampleList()
+
+    onMounted(() => {
+      vueBus.$on('success', (type: string) => {
+        if (type === 'add') {
+          currentChange(1)
+        }
+        getExampleList()
+      })
+    })
+
+    onUnmounted(() => {
+      vueBus.$off('success')
+    })
+
+    return {
+      open,
+      searchData, searchSubmit, resetSubmit,
+      columns,
+      defalutParams,
+      loading,
+      tableData,
+      total,
+      handleSizeChange,
+      handleCurrentChange,
+      handleSelectionChange,
+      dels
+    }
+  }
+})
+</script>
+
+<style>
+</style>

+ 13 - 0
src/vue-bus/index.ts

@@ -0,0 +1,13 @@
+// 通过mitt实现vue-bus通信
+
+import mitt from 'mitt'
+
+const bus: any = {}
+
+const emitter = mitt()
+
+bus.$on = emitter.on
+bus.$off = emitter.off
+bus.$emit = emitter.emit
+
+export default bus

+ 4 - 4
yarn.lock

@@ -3990,10 +3990,10 @@ electron-to-chromium@^1.3.621:
   resolved "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.622.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.622.tgz#9726bd2e67a5462154750ce9701ca6af07d07877"
   integrity sha1-lya9LmelRiFUdQzpcBymrwfQeHc=
 
-element-plus@1.0.1-beta.11:
-  version "1.0.1-beta.11"
-  resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.0.1-beta.11.tgz#bcced187b44748b8d597c2681923c5097ee95fbe"
-  integrity sha512-qmael6DKlhJsOaEi/n7Zpw10qOCRO1irCXJ1LO8eqtl/7UCPZQNiOIIu6Gd1SgsJWbiW55NrFbHiRpfx37ZLIg==
+element-plus@1.0.1-beta.14:
+  version "1.0.1-beta.14"
+  resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.0.1-beta.14.tgz#67e6742ef0a380156d306d519d474220f8c3e03e"
+  integrity sha512-iqc8lAmj4yYTVQFlxwm5IWj3vxufgmF8FVwKgEKJfy1qQQVqA34R81IgywQpYh3jO/d+ofmHXhsm+z3ojXVp0A==
   dependencies:
     "@popperjs/core" "^2.4.4"
     async-validator "^3.4.0"