1. yarn add -g @haha-cli/core
  2. haha-cli --version
  3. haha-cli init hahaya //备注 上线仍有问题 暂不处理 先主要处理项目

Git Flow标准

根据需求,从 master 拉出分支
开发阶段,提交 commit
开发完毕,发起 PR(pull request)
代码评审
部署,测试
merge 到 master
分支命名
feature 开头代表新功能开发
hotfix 开头代表 bug 修复
安装第三方组件库 ant-design-vue
yarn add ant-design-vue

  1. import { createApp } from 'vue'
  2. import App from './App.vue'
  3. import Antd from 'ant-design-vue'
  4. import 'ant-design-vue/dist/antd.css'
  5. createApp(App).use(Antd).mount('#app')

增加ts配置:https://juejin.cn/post/6982444129271676942

项目搭建

  1. <template>
  2. <div class="editor-container">
  3. <a-layout>
  4. <a-layout-sider width="300" style="background: #fff">
  5. <div class="sidebar-container">组件列表</div>
  6. </a-layout-sider>
  7. <a-layout style="padding: 0 24px 24px">
  8. <a-layout-content class="preview-container">
  9. <p>画布区域</p>
  10. <div class="preview-list" id="canvas-area"></div>
  11. </a-layout-content>
  12. </a-layout>
  13. <a-layout-sider
  14. width="300"
  15. style="background: #fff"
  16. class="settings-panel"
  17. >
  18. 组件属性
  19. </a-layout-sider>
  20. </a-layout>
  21. </div>
  22. </template>
  23. <script lang="ts">
  24. import { defineComponent, computed } from 'vue'
  25. export default defineComponent({
  26. components: {},
  27. setup() {},
  28. })
  29. </script>
  30. <style>
  31. .editor-container .preview-container {
  32. padding: 24px;
  33. margin: 0;
  34. min-height: 85vh;
  35. display: flex;
  36. flex-direction: column;
  37. align-items: center;
  38. position: relative;
  39. }
  40. .editor-container .preview-list {
  41. padding: 0;
  42. margin: 0;
  43. min-width: 375px;
  44. min-height: 200px;
  45. border: 1px solid #efefef;
  46. background: #fff;
  47. overflow-x: hidden;
  48. overflow-y: auto;
  49. position: fixed;
  50. margin-top: 50px;
  51. max-height: 80vh;
  52. }
  53. </style>
  1. <template>
  2. <a-layout>
  3. <a-layout-header>乐高</a-layout-header>
  4. <a-layout-content>
  5. <div class="home-container"><template-list></template-list></div
  6. ></a-layout-content>
  7. <a-layout-footer 慕课网(imooc.com)版权所有</a-layout-footer>
  8. </a-layout>
  9. </template>
  10. <script>
  11. import TemplateList from '@/components/TemplateList.vue'
  12. import { defineComponent } from 'vue'
  13. export default defineComponent({
  14. components: { TemplateList },
  15. setup() {
  16. },
  17. })
  18. </script>
  19. <style>
  20. .ant-layout-header {
  21. color: white;
  22. }
  23. .page-title {
  24. color: #fff;
  25. }
  26. .home-container {
  27. background: #fff;
  28. padding: 0 24px 24px 30px;
  29. min-height: 85vh;
  30. max-width: 1200px;
  31. /* margin: 50px auto; */
  32. width: 100%;
  33. }
  34. </style>
  1. <template>
  2. <div class="homepage-container">
  3. <a-layout :style="{ background: '#fff' }">
  4. <a-layout-header class="header">
  5. <div class="page-title">
  6. <router-link to="/">慕课乐高</router-link>
  7. </div>
  8. <user-profile :user="user"></user-profile>
  9. </a-layout-header>
  10. <a-layout-content class="home-layout">
  11. <router-view></router-view>
  12. </a-layout-content>
  13. </a-layout>
  14. <a-layout-footer>
  15. © 慕课网(imooc.com)版权所有 | ICP20000929号-2
  16. </a-layout-footer>
  17. </div>
  18. </template>
  19. <script lang="ts">
  20. import { computed, defineComponent } from 'vue'
  21. export default defineComponent({
  22. name: 'Index',
  23. components: {},
  24. setup() {},
  25. })
  26. </script>
  27. <style>
  28. .header {
  29. display: flex;
  30. justify-content: space-between;
  31. align-items: center;
  32. }
  33. .page-title {
  34. color: #fff;
  35. }
  36. </style>
  1. <template>
  2. <div class="work-detail-container">
  3. <a-row type="flex" justify="center">
  4. <a-col :span="8" class="cover-img">
  5. <img
  6. src="http://typescript-vue.oss-cn-beijing.aliyuncs.com/vue-marker/5f81cca3f3bf7a0e1ebaf885.png"
  7. alt=""
  8. />
  9. </a-col>
  10. <a-col :span="8">
  11. <h2>11</h2>
  12. <p>11</p>
  13. <div class="author">
  14. <a-avatar>V</a-avatar>
  15. 该模版由 <b>11</b> 创作
  16. </div>
  17. <div class="bar-code-area">
  18. <span>扫一扫,手机预览</span>
  19. <div ref="container"></div>
  20. </div>
  21. <div class="use-button">
  22. <a-button type="primary" size="large"> 使用模版 </a-button>
  23. <a-button size="large"> 下载图片海报 </a-button>
  24. </div>
  25. </a-col>
  26. </a-row>
  27. </div>
  28. </template>
  29. <script lang="ts">
  30. import { defineComponent, computed } from 'vue'
  31. export default defineComponent({
  32. setup() {},
  33. })
  34. </script>
  35. <style scoped>
  36. .work-detail-container {
  37. margin-top: 50px;
  38. }
  39. .cover-img {
  40. margin-right: 30px;
  41. }
  42. .cover-img img {
  43. width: 100%;
  44. }
  45. .use-button {
  46. margin: 30px 0;
  47. }
  48. .ant-avatar {
  49. margin-right: 10px;
  50. }
  51. .bar-code-area {
  52. margin: 20px 0;
  53. }
  54. </style>
  1. <template>
  2. <div class="template-list-component">
  3. <a-row :gutter="16">
  4. <a-col :span="6" class="poster-item">
  5. <a-card hoverable>
  6. <template #cover>
  7. <!-- <img :src="item.coverImg" v-if="item.coverImg" /> -->
  8. <img
  9. src="http://typescript-vue.oss-cn-beijing.aliyuncs.com/vue-marker/5f81cca3f3bf7a0e1ebaf885.png"
  10. />
  11. <div class="hover-item">
  12. <a-button size="large" type="primary">使用该模版创建</a-button>
  13. </div>
  14. </template>
  15. <a-card-meta :title="11">
  16. <template v-slot:description>
  17. <div class="description-detail">
  18. <span>作者:11</span>
  19. <span class="user-number">11</span>
  20. </div>
  21. </template>
  22. </a-card-meta>
  23. </a-card>
  24. </a-col>
  25. </a-row>
  26. </div>
  27. </template>
  28. <script lang="ts">
  29. import { defineComponent } from 'vue'
  30. export default defineComponent({
  31. name: 'template-list',
  32. })
  33. </script>
  34. <style>
  35. .poster-item {
  36. position: relative;
  37. margin-bottom: 20px;
  38. }
  39. .poster-item .ant-card {
  40. border-radius: 12px;
  41. }
  42. .poster-item .ant-card-cover {
  43. height: 390px;
  44. }
  45. .poster-item .ant-card-cover > img {
  46. width: 100%;
  47. }
  48. .poster-item .ant-card-hoverable {
  49. box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.1);
  50. }
  51. .poster-item .ant-card-body {
  52. padding: 0;
  53. }
  54. .poster-item .ant-card-meta {
  55. margin: 0;
  56. }
  57. .poster-item .ant-card-meta-title {
  58. color: #333;
  59. padding: 10px 12px;
  60. border-bottom: 1px solid #f2f2f2;
  61. margin-bottom: 0 !important;
  62. }
  63. .description-detail {
  64. display: flex;
  65. justify-content: space-between;
  66. padding: 13px 12px;
  67. color: #999;
  68. }
  69. .user-number {
  70. font-weight: bold;
  71. }
  72. .poster-title {
  73. height: 70px;
  74. }
  75. .poster-title h2 {
  76. margin-bottom: 0px;
  77. }
  78. .poster-item .ant-card-cover {
  79. position: relative;
  80. overflow: hidden;
  81. border-top-left-radius: 12px;
  82. border-top-right-radius: 12px;
  83. }
  84. .poster-item .ant-card-cover img {
  85. transition: all ease-in 0.2s;
  86. }
  87. .poster-item .ant-card-cover .hover-item {
  88. position: absolute;
  89. left: 0;
  90. top: 0;
  91. width: 100%;
  92. height: 100%;
  93. display: none;
  94. background: rgba(0, 0, 0, 0.8);
  95. align-items: center;
  96. justify-content: center;
  97. border-top-left-radius: 12px;
  98. border-top-right-radius: 12px;
  99. }
  100. .poster-item:hover .hover-item {
  101. display: flex;
  102. }
  103. .poster-item:hover img {
  104. transform: scale(1.25);
  105. }
  106. .barcode-container img {
  107. border-radius: 0;
  108. }
  109. </style>

安装 vue-router 路由

  1. import { createRouter, createWebHashHistory } from 'vue-router'
  2. const Home = () => import('@/views/Home.vue')
  3. const Editor = () => import('../views/Editor.vue')
  4. const TemplateDetail = () => import('../views/TemplateDetail.vue')
  5. const router = createRouter({
  6. history: createWebHashHistory(),
  7. routes: [
  8. {
  9. path: '/',
  10. name: 'home',
  11. component: Home,
  12. meta: {
  13. withFooter: true,
  14. },
  15. children: [
  16. //嵌套路由
  17. // {
  18. // path: '/',
  19. // name: 'home',
  20. // component: Home,
  21. // },
  22. ],
  23. },
  24. {
  25. path: '/editor',
  26. name: 'editor',
  27. component: Editor,
  28. },
  29. {
  30. path: '/template/:id',
  31. name: 'template',
  32. component: TemplateDetail,
  33. meta: {
  34. withFooter: true,
  35. },
  36. },
  37. ],
  38. })
  39. export default router
  1. import { createApp } from 'vue'
  2. import App from './App.vue'
  3. import Antd from 'ant-design-vue'
  4. import 'ant-design-vue/dist/antd.css'
  5. import router from '@/routes/index'
  6. //将路由挂载到app实例上
  7. createApp(App).use(Antd).use(router).mount('#app')

在App.vue增加路由视图

  1. <template>
  2. <router-view />
  3. </template>
  4. <script lang="ts">
  5. import { defineComponent } from 'vue'
  6. export default defineComponent({
  7. name: 'App',
  8. components: {},
  9. })
  10. </script>
  11. <style>
  12. #app {
  13. font-family: Avenir, Helvetica, Arial, sans-serif;
  14. -webkit-font-smoothing: antialiased;
  15. -moz-osx-font-smoothing: grayscale;
  16. text-align: center;
  17. color: #2c3e50;
  18. margin-top: 60px;
  19. }
  20. </style>

增加路由函数实现跳转

  1. ...
  2. //详情页点击详情的使用模板,跳转到编辑页面
  3. <router-link to="/editor">
  4. <a-button type="primary" size="large"> 使用模版 </a-button>
  5. </router-link>
  6. ...
  1. ...
  2. //首页点击使用该模版创建,跳转到详情页面
  3. <router-link :to="`/template/${1}`">
  4. <a-button size="large" type="primary">使用该模版创建</a-button>
  5. </router-link>
  6. ...
  1. <template>
  2. <div class="template-list-component">
  3. <pre>{{ route }}</pre>
  4. ...
  5. </div>
  6. </template>
  7. <script lang="ts">
  8. import { defineComponent } from 'vue'
  9. // useRoute(获取路由参数)
  10. import { useRoute } from 'vue-router'
  11. export default defineComponent({
  12. name: 'template-list',
  13. setup() {
  14. const route = useRoute()
  15. return {
  16. route,
  17. }
  18. },
  19. })
  20. </script>
  21. ...

image.png

  1. ...
  2. <script>
  3. import { useRouter } from 'vue-router'
  4. import TemplateList from '@/components/TemplateList.vue'
  5. import { defineComponent } from 'vue'
  6. export default defineComponent({
  7. components: { TemplateList },
  8. setup() {
  9. //useRouter 跳转
  10. const router = useRouter()
  11. setTimeout(() => {
  12. router.push(`/template/${1}`)
  13. }, 2000)
  14. },
  15. })
  16. </script>
  17. ...

增加header是否根据不同页面显示显示

  1. <template>
  2. <a-layout>
  3. <a-layout-header><span class="header">乐高</span></a-layout-header>
  4. <a-layout-content>
  5. <div class="content-container">
  6. <router-view />
  7. </div>
  8. </a-layout-content>
  9. <a-layout-footer v-if="withFooter"
  10. >© 慕课网(imooc.com)版权所有</a-layout-footer
  11. >
  12. </a-layout>
  13. </template>
  14. <script lang="ts">
  15. import { defineComponent, toRaw, computed } from 'vue'
  16. import { useRoute } from 'vue-router'
  17. export default defineComponent({
  18. name: 'app',
  19. setup() {
  20. const route = useRoute()
  21. const withFooter = computed(() => route.meta.withFooter)
  22. return {
  23. withFooter,
  24. }
  25. },
  26. })
  27. </script>
  28. <style>
  29. .page-title {
  30. color: #fff;
  31. }
  32. .content-container {
  33. background: #fff;
  34. width: 100%;
  35. }
  36. .header {
  37. color: white;
  38. }
  39. </style>

安装vuex

安装less:

路由

  1. import { createRouter, createWebHashHistory } from 'vue-router'
  2. const Index = () => import('@/views/Index.vue')
  3. const Home = () => import('@/views/Home.vue')
  4. const Editor = () => import('../views/Editor.vue')
  5. const TemplateDetail = () => import('../views/TemplateDetail.vue')
  6. const router = createRouter({
  7. history: createWebHashHistory(),
  8. routes: [
  9. {
  10. path: '/',
  11. name: 'home',
  12. component: Index,
  13. children: [
  14. //嵌套路由
  15. {
  16. path: '',
  17. name: 'home',
  18. component: Home,
  19. },
  20. {
  21. path: '/template/:id',
  22. name: 'template',
  23. component: TemplateDetail,
  24. },
  25. ],
  26. },
  27. {
  28. path: '/editor',
  29. name: 'editor',
  30. component: Editor,
  31. },
  32. ],
  33. })
  34. export default router

store

  1. export const LOGIN = 'login'
  2. export const LOGOUT = 'logout'
  3. export const GETTEMPLATEBYID = 'getTemplateById'
  1. import { Module } from 'vuex'
  2. import { GloabalDataProps } from '.'
  3. import { GETTEMPLATEBYID } from './mutation-types'
  4. export interface TemplateProps {
  5. id: number
  6. title: string
  7. coverImg: string
  8. author: string
  9. copiedCount: number
  10. }
  11. export const testData: TemplateProps[] = [
  12. {
  13. id: 1,
  14. coverImg:
  15. 'https://static.imooc-lego.com/upload-files/screenshot-889755.png',
  16. title: 'test title 1',
  17. author: 'viking',
  18. copiedCount: 1,
  19. },
  20. {
  21. id: 2,
  22. coverImg:
  23. 'https://static.imooc-lego.com/upload-files/screenshot-677311.png',
  24. title: '前端架构师直播海报',
  25. author: 'viking',
  26. copiedCount: 1,
  27. },
  28. {
  29. id: 3,
  30. coverImg:
  31. 'https://static.imooc-lego.com/upload-files/screenshot-682056.png',
  32. title: '前端架构师直播海报',
  33. author: 'viking',
  34. copiedCount: 1,
  35. },
  36. {
  37. id: 4,
  38. coverImg:
  39. 'https://static.imooc-lego.com/upload-files/screenshot-677311.png',
  40. title: '前端架构师直播海报',
  41. author: 'viking',
  42. copiedCount: 1,
  43. },
  44. {
  45. id: 5,
  46. coverImg:
  47. 'https://static.imooc-lego.com/upload-files/screenshot-889755.png',
  48. title: '前端架构师直播海报',
  49. author: 'viking',
  50. copiedCount: 1,
  51. },
  52. {
  53. id: 6,
  54. coverImg:
  55. 'https://static.imooc-lego.com/upload-files/screenshot-677311.png',
  56. title: '前端架构师直播海报',
  57. author: 'viking',
  58. copiedCount: 1,
  59. },
  60. ]
  61. export interface TemplatesProps {
  62. templateList: TemplateProps[]
  63. }
  64. //两个参数 第一个本地的interface,第二个是全局的interface
  65. const templates: Module<TemplatesProps, GloabalDataProps> = {
  66. state: {
  67. templateList: testData,
  68. },
  69. getters: {
  70. [GETTEMPLATEBYID](state, getters, rootState) {
  71. return (templateId: number) =>
  72. state.templateList.find((item) => item.id === templateId)
  73. },
  74. },
  75. }
  76. export default templates
  1. import { Module } from 'vuex'
  2. import { GloabalDataProps } from '.'
  3. import { LOGIN, LOGOUT } from './mutation-types'
  4. export interface UserProps {
  5. isLogin: boolean
  6. userName?: string
  7. }
  8. const testUser: UserProps = { isLogin: false }
  9. //两个参数 第一个本地的interface,第二个是全局的interface
  10. const user: Module<UserProps, GloabalDataProps> = {
  11. mutations: {
  12. [LOGIN](state) {
  13. state.isLogin = true
  14. state.userName = 'hahaya'
  15. },
  16. [LOGOUT](state) {
  17. state.isLogin = false
  18. },
  19. },
  20. }
  21. export default user
  1. import { createStore } from 'vuex'
  2. import user, { UserProps } from './user'
  3. import templates, { TemplatesProps } from './templates'
  4. export interface GloabalDataProps {
  5. user: UserProps
  6. templates: TemplatesProps
  7. }
  8. const store = createStore<GloabalDataProps>({
  9. modules: {
  10. user,
  11. templates,
  12. },
  13. })
  14. export default store

View

  1. <template>
  2. <div class="home-container">
  3. <template-list :templates="{ templates }"></template-list>
  4. </div>
  5. </template>
  6. <script lang="ts">
  7. import { useRouter } from 'vue-router'
  8. import TemplateList from '@/components/TemplateList.vue'
  9. import { computed, defineComponent, toRaw } from 'vue'
  10. import { GloabalDataProps } from '@/store'
  11. import { useStore } from 'vuex'
  12. import { TemplateProps } from '@/store/templates'
  13. export default defineComponent({
  14. components: { TemplateList },
  15. setup() {
  16. const store = useStore<GloabalDataProps>()
  17. const templates = computed(() => store.state.templates.templateList)
  18. return {
  19. templates,
  20. }
  21. },
  22. })
  23. </script>
  24. <style lang="less" scoped>
  25. .page-title {
  26. color: #fff;
  27. }
  28. .home-container {
  29. background: #fff;
  30. padding: 0 24px 24px 30px;
  31. min-height: 85vh;
  32. max-width: 1200px;
  33. /* margin: 50px auto; */
  34. width: 100%;
  35. }
  36. </style>
  1. <template>
  2. <a-layout>
  3. <a-layout-header>
  4. <router-link to="/">慕课乐高</router-link>
  5. <user-profile :user="{ user }" />
  6. </a-layout-header>
  7. <a-layout-content>
  8. <div class="content-container">
  9. <router-view />
  10. </div>
  11. </a-layout-content>
  12. <a-layout-footer> © 慕课网(imooc.com)版权所有 </a-layout-footer>
  13. </a-layout>
  14. </template>
  15. <script lang="ts">
  16. import { defineComponent, toRaw, computed } from 'vue'
  17. import { useRoute } from 'vue-router'
  18. import UserProfile from '@/components/UserProfile.vue'
  19. import { GloabalDataProps } from '@/store'
  20. import { useStore } from 'vuex'
  21. export default defineComponent({
  22. name: 'index',
  23. components: { UserProfile },
  24. setup() {
  25. const route = useRoute()
  26. const withHeader = computed(() => route.meta.withHeader)
  27. const store = useStore<GloabalDataProps>()
  28. const user = computed(() => store.state.user)
  29. return {
  30. withHeader,
  31. user: user,
  32. }
  33. },
  34. })
  35. </script>
  36. <style lang="less" scoped>
  37. .content-container {
  38. width: 100%;
  39. padding: 20px;
  40. background: #fff;
  41. }
  42. .ant-layout-header {
  43. display: flex;
  44. justify-content: space-between;
  45. align-items: center;
  46. }
  47. </style>
  1. <template>
  2. <div class="work-detail-container">
  3. <a-row type="flex" justify="center">
  4. <a-col :span="8" class="cover-img">
  5. <img :src="template.coverImg" alt="" />
  6. </a-col>
  7. <a-col :span="8">
  8. <h2>{{ template.title }}</h2>
  9. <div class="author">
  10. <a-avatar>V</a-avatar>
  11. 该模版由 <b>{{ template.author }}</b> 创作
  12. </div>
  13. <div class="bar-code-area">
  14. <span>扫一扫,手机预览</span>
  15. <div ref="container"></div>
  16. </div>
  17. <div class="use-button">
  18. <router-link to="/editor">
  19. <a-button type="primary" size="large"> 使用模版 </a-button>
  20. </router-link>
  21. <a-button size="large"> 下载图片海报 </a-button>
  22. </div>
  23. </a-col>
  24. </a-row>
  25. </div>
  26. </template>
  27. <script lang="ts">
  28. import { GloabalDataProps } from '@/store'
  29. import { GETTEMPLATEBYID } from '@/store/mutation-types'
  30. import { TemplateProps } from '@/store/templates'
  31. import { defineComponent, computed } from 'vue'
  32. import { useStore } from 'vuex'
  33. import { useRoute } from 'vue-router'
  34. export default defineComponent({
  35. setup() {
  36. const store = useStore<GloabalDataProps>()
  37. const route = useRoute()
  38. const template = computed<TemplateProps>(() =>
  39. store.getters[GETTEMPLATEBYID](Number(route.params.id)),
  40. )
  41. return {
  42. template,
  43. }
  44. },
  45. })
  46. </script>
  47. <style lang="less" scoped>
  48. .work-detail-container {
  49. margin-top: 50px;
  50. }
  51. .cover-img {
  52. margin-right: 30px;
  53. }
  54. .cover-img img {
  55. width: 100%;
  56. }
  57. .use-button {
  58. margin: 30px 0;
  59. }
  60. .ant-avatar {
  61. margin-right: 10px;
  62. }
  63. .bar-code-area {
  64. margin: 20px 0;
  65. }
  66. </style>

components

  1. <template>
  2. <div class="template-list-component">
  3. <a-row :gutter="16">
  4. <a-col
  5. :span="6"
  6. class="poster-item"
  7. v-for="item in templates"
  8. :key="item.id"
  9. >
  10. <a-card hoverable>
  11. <template #cover>
  12. <img :src="item.coverImg" v-if="item.coverImg" />
  13. <img
  14. v-else
  15. src="http://typescript-vue.oss-cn-beijing.aliyuncs.com/vue-marker/5f81cca3f3bf7a0e1ebaf885.png"
  16. />
  17. <div class="hover-item">
  18. <router-link :to="`/template/${item.id}`">
  19. <a-button size="large" type="primary">使用该模版创建</a-button>
  20. </router-link>
  21. </div>
  22. </template>
  23. <a-card-meta :title="item.title">
  24. <template v-slot:description>
  25. <div class="description-detail">
  26. <span>作者:{{ item.author }}</span>
  27. <span class="user-number">{{ item.copiedCount }}</span>
  28. </div>
  29. </template>
  30. </a-card-meta>
  31. </a-card>
  32. </a-col>
  33. </a-row>
  34. </div>
  35. </template>
  36. <script lang="ts">
  37. import { computed, defineComponent, PropType, reactive } from 'vue'
  38. // useRoute(获取路由参数)
  39. import { useRoute } from 'vue-router'
  40. import { TemplateProps, TemplatesProps } from '@/store/templates'
  41. export default defineComponent({
  42. name: 'template-list',
  43. props: {
  44. templates: {
  45. type: Object,
  46. required: true,
  47. },
  48. },
  49. setup(props) {
  50. const route = useRoute()
  51. const templates = computed<TemplateProps[]>(() => props.templates.templates)
  52. return {
  53. route,
  54. templates,
  55. }
  56. },
  57. })
  58. </script>
  59. <style lang="less" scoped>
  60. .poster-item {
  61. position: relative;
  62. margin-bottom: 20px;
  63. }
  64. .poster-item .ant-card {
  65. border-radius: 12px;
  66. }
  67. .poster-item .ant-card-cover {
  68. height: 390px;
  69. }
  70. .poster-item .ant-card-cover > img {
  71. width: 100%;
  72. }
  73. .poster-item .ant-card-hoverable {
  74. box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.1);
  75. }
  76. .poster-item .ant-card-body {
  77. padding: 0;
  78. }
  79. .poster-item .ant-card-meta {
  80. margin: 0;
  81. }
  82. .poster-item .ant-card-meta-title {
  83. color: #333;
  84. padding: 10px 12px;
  85. border-bottom: 1px solid #f2f2f2;
  86. margin-bottom: 0 !important;
  87. }
  88. .description-detail {
  89. display: flex;
  90. justify-content: space-between;
  91. padding: 13px 12px;
  92. color: #999;
  93. }
  94. .user-number {
  95. font-weight: bold;
  96. }
  97. .poster-title {
  98. height: 70px;
  99. }
  100. .poster-title h2 {
  101. margin-bottom: 0px;
  102. }
  103. .poster-item .ant-card-cover {
  104. position: relative;
  105. overflow: hidden;
  106. border-top-left-radius: 12px;
  107. border-top-right-radius: 12px;
  108. }
  109. .poster-item .ant-card-cover img {
  110. transition: all ease-in 0.2s;
  111. }
  112. .poster-item .ant-card-cover .hover-item {
  113. position: absolute;
  114. left: 0;
  115. top: 0;
  116. width: 100%;
  117. height: 100%;
  118. display: none;
  119. background: rgba(0, 0, 0, 0.8);
  120. align-items: center;
  121. justify-content: center;
  122. border-top-left-radius: 12px;
  123. border-top-right-radius: 12px;
  124. }
  125. .poster-item:hover .hover-item {
  126. display: flex;
  127. }
  128. .poster-item:hover img {
  129. transform: scale(1.25);
  130. }
  131. .barcode-container img {
  132. border-radius: 0;
  133. }
  134. </style>
  1. <template>
  2. <a-button
  3. type="primary"
  4. class="user-profile-component"
  5. v-if="!user.isLogin"
  6. @click="login"
  7. >
  8. 登录
  9. </a-button>
  10. <div v-else>
  11. <a-dropdown-button class="user-profile-component" type="primary">
  12. {{ user.userName }}
  13. <template #overlay>
  14. <a-menu class="user-profile-dropdown">
  15. <a-menu-item key="1" @click="logout">
  16. <user-outlined />
  17. 退出登录
  18. </a-menu-item>
  19. </a-menu>
  20. </template>
  21. </a-dropdown-button>
  22. </div>
  23. </template>
  24. <script lang="ts">
  25. import { useStore } from 'vuex'
  26. import { computed, defineComponent, PropType } from 'vue'
  27. import { useRouter } from 'vue-router'
  28. import { UserProps } from '@/store/user'
  29. import { UserOutlined } from '@ant-design/icons-vue'
  30. import { message } from 'ant-design-vue'
  31. import { GloabalDataProps } from '@/store'
  32. export default defineComponent({
  33. name: 'user-profile',
  34. components: {
  35. UserOutlined,
  36. },
  37. props: {
  38. user: {
  39. type: Object,
  40. required: true,
  41. },
  42. },
  43. setup(props) {
  44. const store = useStore<GloabalDataProps>()
  45. const router = useRouter()
  46. //登录
  47. const login = () => {
  48. console.log()
  49. store.commit('login')
  50. message.success('登录成功')
  51. }
  52. //登出
  53. const logout = () => {
  54. store.commit('logout')
  55. message.success('退出登录成功,2秒后跳转到首页', 2)
  56. setTimeout(() => {
  57. router.replace('/')
  58. }, 2000)
  59. }
  60. return {
  61. user: props.user.user as UserProps,
  62. login,
  63. logout,
  64. }
  65. },
  66. })
  67. </script>
  68. <style lang="less" scoped>
  69. .user-profile-dropdown {
  70. border-radius: 2px !important;
  71. }
  72. .user-operation > * {
  73. margin-left: 30px !important;
  74. }
  75. </style>