电商商城定制开发Vben Admin

 //项目结构

  1. .
  2. ├── build # 电商商城定制开发打包脚本相关
  3. │ ├── config # 配置文件
  4. │ ├── generate # 生成器
  5. │ ├── script # 脚本
  6. │ └── vite # vite配置
  7. ├── mock # mock文件夹
  8. ├── public # 电商商城定制开发公共静态资源目录
  9. ├── src # 主目录
  10. │ ├── api # 接口文件
  11. │ ├── assets # 资源文件
  12. │ │ ├── icons # icon sprite 电商商城定制开发图标文件夹
  13. │ │ ├── images # 电商商城定制开发项目存放电商商城定制开发图片的文件夹
  14. │ │ └── svg # 项目存放svg图片的文件夹
  15. │ ├── components # 公共组件
  16. │ ├── design # 样式文件
  17. │ ├── directives # 指令
  18. │ ├── enums # 枚举/常量
  19. │ ├── hooks # hook
  20. │ │ ├── component # 组件相关hook
  21. │ │ ├── core # 基础hook
  22. │ │ ├── event # 事件相关hook
  23. │ │ ├── setting # 配置相关hook
  24. │ │ └── web # web相关hook
  25. │ ├── layouts # 布局文件
  26. │ │ ├── default # 默认布局
  27. │ │ ├── iframe # iframe布局
  28. │ │ └── page # 页面布局
  29. │ ├── locales # 多语言
  30. │ ├── logics # 逻辑
  31. │ ├── main.ts # 主入口
  32. │ ├── router # 路由配置
  33. │ ├── settings # 项目配置
  34. │ │ ├── componentSetting.ts # 组件配置
  35. │ │ ├── designSetting.ts # 样式配置
  36. │ │ ├── encryptionSetting.ts # 加密配置
  37. │ │ ├── localeSetting.ts # 电商商城定制开发多语言配置
  38. │ │ ├── projectSetting.ts # 项目配置
  39. │ │ └── siteSetting.ts # 站点配置
  40. │ ├── store # 数据仓库
  41. │ ├── utils # 工具类
  42. │ └── views # 页面
  43. ├── test # 测试
  44. │ └── server # 电商商城定制开发测试用到的服务
  45. │ ├── api # 电商商城定制开发测试服务器
  46. │ ├── upload # 电商商城定制开发测试上传服务器
  47. │ └── websocket # 测试ws服务器
  48. ├── types # 类型文件
  49. ├── vite.config.ts # vite配置文件
  50. └── windi.config.ts # windcss配置文件

 //路由

电商商城定制开发项目存放于  下面。 电商商城定制开发用于存放路由模块,电商商城定制开发在该目录下的文件会自动注册。

//

在浏览器支持 ES 模块之前,JavaScript 并没有提供的原生机制让开发者以模块化的方式进行开发。这也正是我们对 “打包” 这个概念熟悉的原因:使用工具抓取、处理并将我们的源码模块串联成可以在浏览器中运行的文件。

时过境迁,我们见证了诸如 、 和  等工具的变迁,它们极大地改善了前端开发者的开发体验。

然而,当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。我们开始遇到性能瓶颈 —— 使用 JavaScript 开发的工具通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用 HMR,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。

Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。

// .env

  1. # port 端口
  2. VITE_PORT = 3100
  3. # spa-title 名字
  4. VITE_GLOB_APP_TITLE = Vben Admin
  5. # spa shortname 轻应用
  6. VITE_GLOB_APP_SHORT_NAME = vue_vben_admin

//  .env.development 开发环境下的配置文件

  1. # Whether to open mock 是否打开mock
  2. VITE_USE_MOCK = true
  3. # public path 公共通道
  4. VITE_PUBLIC_PATH = /
  5. # Cross-domain proxy, you can configure multiple 跨域代理,可以配置多个
  6. # Please note that no line breaks 请注意不要换行
  7. VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","http://localhost:3300/upload"]]
  8. # VITE_PROXY=[["/api","https://vvbin.cn/test"]]
  9. # Delete console 删除控制台
  10. VITE_DROP_CONSOLE = false
  11. # Basic interface address SPA 基本接口地址
  12. VITE_GLOB_API_URL=/basic-api
  13. # File upload address, optional 文件上传地址,可选
  14. VITE_GLOB_UPLOAD_URL=/upload
  15. # Interface prefix 接口前缀
  16. VITE_GLOB_API_URL_PREFIX=

 // .env.production 生产环境下的配置文件

  1. # Whether to open mock 是否开启mock
  2. VITE_USE_MOCK = true
  3. # public path 公共路径
  4. VITE_PUBLIC_PATH = /
  5. # Delete console 删除控制台
  6. VITE_DROP_CONSOLE = true
  7. # Whether to enable gzip or brotli compression 是否启用gzip或brotli压缩
  8. # Optional: gzip | brotli | none 可选:gzip | brotli | none
  9. # If you need multiple forms, you can use `,` to separate 如果需要多个表单,可以使用“,”分隔
  10. VITE_BUILD_COMPRESS = 'none'
  11. # Whether to delete origin files when using compress, default false 使用压缩时是否删除源文件,默认为false
  12. VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
  13. # Basic interface address SPA 基本接口地址
  14. VITE_GLOB_API_URL=/basic-api
  15. # File upload address, optional 文件上传地址,可选
  16. # It can be forwarded by nginx or write the actual address directly 它可以通过nginx转发,也可以直接写入实际地址
  17. VITE_GLOB_UPLOAD_URL=/upload
  18. # Interface prefix 接口前缀
  19. VITE_GLOB_API_URL_PREFIX=
  20. # Whether to enable image compression 是否启用图像压缩
  21. VITE_USE_IMAGEMIN= true
  22. # use pwa 使用pwa
  23. VITE_USE_PWA = false
  24. # Is it compatible with older browsers 它与旧浏览器兼容吗
  25. VITE_LEGACY = false

// .env.test  测试环境

  1. # 通常这个变量用来区分开发与生产环境,加载不同的配置。
  2. NODE_ENV=production
  3. # Whether to open mock 是否打开模拟
  4. VITE_USE_MOCK = true
  5. # public path
  6. VITE_PUBLIC_PATH = /
  7. # Delete console 公共通道
  8. VITE_DROP_CONSOLE = true
  9. # Whether to enable gzip or brotli compression 是否启用gzip或brotli压缩
  10. # Optional: gzip | brotli | none 可选:gzip | brotli | none
  11. # If you need multiple forms, you can use `,` to separate 如果需要多个表单,可以使用“,”分隔
  12. VITE_BUILD_COMPRESS = 'none'
  13. # Whether to delete origin files when using compress, default false 使用压缩时是否删除源文件,默认为false
  14. VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
  15. # Basic interface address SPA 基本接口地址
  16. VITE_GLOB_API_URL=/basic-api
  17. # File upload address, optional 文件上传地址,可选
  18. # It can be forwarded by nginx or write the actual address directly 它可以通过nginx转发,也可以直接写入实际地址
  19. VITE_GLOB_UPLOAD_URL=/upload
  20. # Interface prefix 接口前缀
  21. VITE_GLOB_API_URL_PREFIX=
  22. # Whether to enable image compression 是否启用图像压缩
  23. VITE_USE_IMAGEMIN= true
  24. # use pwa 使用pwa
  25. VITE_USE_PWA = false
  26. # Is it compatible with older browsers 它与旧浏览器兼容吗
  27. VITE_LEGACY = false

 //部分权限 路径Vben Admin\vue-vben-admin\src\settings\projectSetting.ts

  1. import type { ProjectConfig } from '/#/config';
  2. import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '/@/enums/menuEnum';
  3. import { CacheTypeEnum } from '/@/enums/cacheEnum';
  4. import {
  5. ContentEnum,
  6. PermissionModeEnum,
  7. ThemeEnum,
  8. RouterTransitionEnum,
  9. SettingButtonPositionEnum,
  10. SessionTimeoutProcessingEnum,
  11. } from '/@/enums/appEnum';
  12. import { SIDE_BAR_BG_COLOR_LIST, HEADER_PRESET_BG_COLOR_LIST } from './designSetting';
  13. import { primaryColor } from '../../build/config/themeConfig';
  14. // ! You need to clear the browser cache after the change
  15. const setting: ProjectConfig = {
  16. // Whether to show the configuration button 是否显示配置按钮
  17. showSettingButton: true,
  18. // Whether to show the theme switch button 是否显示主题切换按钮
  19. showDarkModeToggle: true,
  20. // `Settings` button position` 设置按钮位置
  21. settingButtonPosition: SettingButtonPositionEnum.AUTO,
  22. // Permission mode 权限模式
  23. permissionMode: PermissionModeEnum.ROUTE_MAPPING,
  24. // Permission-related cache is stored in sessionStorage or localStorage
  25. //与权限相关的缓存存储在sessionStorage或localStorage中
  26. permissionCacheType: CacheTypeEnum.LOCAL,
  27. // Session timeout processing 会话超时处理
  28. sessionTimeoutProcessing: SessionTimeoutProcessingEnum.ROUTE_JUMP,
  29. // color颜色
  30. themeColor: primaryColor,
  31. // Website gray mode, open for possible mourning dates 网站灰色模式,为可能的哀悼日开放
  32. grayMode: false,
  33. // Color Weakness Mode 弱色模式
  34. colorWeak: false,
  35. // Whether to cancel the menu, the top, the multi-tab page display, for possible embedded in other systems
  36. //是否取消菜单、顶部、多选项卡页面显示,以便可能嵌入其他系统
  37. fullContent: false,
  38. // content mode内容模式
  39. contentMode: ContentEnum.FULL,
  40. // Whether to display the logo 是否显示logo
  41. showLogo: true,
  42. // Whether to show footer 是否显示页脚
  43. showFooter: false,
  44. // Header configuration 标题配置
  45. headerSetting: {
  46. // header bg color 标题背景颜色
  47. bgColor: HEADER_PRESET_BG_COLOR_LIST[0],
  48. // Fixed at the top 固定在顶部
  49. fixed: true,
  50. // Whether to show top 是否显示顶部
  51. show: true,
  52. // theme主题
  53. theme: ThemeEnum.LIGHT,
  54. // Whether to enable the lock screen function 是否启用锁屏功能
  55. useLockPage: true,
  56. // Whether to show the full screen button 是否显示全屏按钮
  57. showFullScreen: true,
  58. // Whether to show the document button 是否显示文档按钮
  59. showDoc: true,
  60. // Whether to show the notification button 是否显示通知按钮
  61. showNotice: true,
  62. // Whether to display the menu search 是否显示菜单搜索
  63. showSearch: true,
  64. },
  65. // Menu configuration菜单配置
  66. menuSetting: {
  67. // sidebar menu bg color边栏菜单bg颜色
  68. bgColor: SIDE_BAR_BG_COLOR_LIST[0],
  69. // Whether to fix the left menu 是否修复左侧菜单
  70. fixed: true,
  71. // Menu collapse菜单折叠
  72. collapsed: false,
  73. // Whether to display the menu name when folding the menu 折叠菜单时是否显示菜单名称
  74. collapsedShowTitle: false,
  75. // Whether it can be dragged 是否可以拖动
  76. // Only limited to the opening of the left menu, the mouse has a drag bar on the right side of the menu
  77. //鼠标仅限于打开左侧菜单,在菜单右侧有一个拖动条
  78. canDrag: false,
  79. // Whether to show no dom 是否显示没有dom
  80. show: true,
  81. // Whether to show dom 是否显示dom
  82. hidden: false,
  83. // Menu width 菜单宽度
  84. menuWidth: 210,
  85. // Menu mode 菜单模式
  86. mode: MenuModeEnum.INLINE,
  87. // Menu type 菜单类型
  88. type: MenuTypeEnum.SIDEBAR,
  89. // Menu theme 菜单主题
  90. theme: ThemeEnum.DARK,
  91. // Split menu 分割菜单
  92. split: false,
  93. // Top menu layout 顶部菜单布局
  94. topMenuAlign: 'center',
  95. // Fold trigger position 折叠触发位置
  96. trigger: TriggerEnum.HEADER,
  97. // Turn on accordion mode, only show a menu 打开手风琴模式,只显示菜单
  98. accordion: true,
  99. // Switch page to close menu 将页面切换到关闭菜单
  100. closeMixSidebarOnChange: false,
  101. // Module opening method ‘click’ |'hover' 模块打开方法“点击”|“悬停”
  102. mixSideTrigger: MixSidebarTriggerEnum.CLICK,
  103. // Fixed expanded menu 修复了扩展菜单
  104. mixSideFixed: false,
  105. },
  106. // Multi-label 多标签
  107. multiTabsSetting: {
  108. cache: false,
  109. // Turn on 打开
  110. show: true,
  111. // Is it possible to drag and drop sorting tabs 可以拖放排序选项卡吗
  112. canDrag: true,
  113. // Turn on quick actions 开启快速行动
  114. showQuick: true,
  115. // Whether to show the refresh button 是否显示刷新按钮
  116. showRedo: true,
  117. // Whether to show the collapse button 是否显示折叠按钮
  118. showFold: true,
  119. },
  120. // Transition Setting
  121. transitionSetting: {
  122. // Whether to open the page switching animation 是否打开页面切换动画
  123. // The disabled state will also disable pageLoading 禁用状态也将禁用页面加载
  124. enable: true,
  125. // Route basic switching animation 路由基本切换动画
  126. basicTransition: RouterTransitionEnum.FADE_SIDE,
  127. // Whether to open page switching loading 是否打开页面切换加载
  128. // Only open when enable=true 仅当enable=true时打开
  129. openPageLoading: true,
  130. // Whether to open the top progress bar 是否打开顶部进度条
  131. openNProgress: false,
  132. },
  133. // Whether to enable KeepAlive cache is best to close during development, otherwise the cache needs to be cleared every time
  134. //是否启用KeepAlive缓存最好在开发期间关闭,否则每次都需要清除缓存
  135. openKeepAlive: true,
  136. // Automatic screen lock time, 0 does not lock the screen. Unit minute default 0
  137. //自动屏幕锁定时间,0不锁定屏幕。单位分钟默认值0
  138. lockTime: 0,
  139. // Whether to show breadcrumbs 是否显示面包屑
  140. showBreadCrumb: true,
  141. // Whether to show the breadcrumb icon 是否显示面包屑图标
  142. showBreadCrumbIcon: false,
  143. // Use error-handler-plugin 使用错误处理插件
  144. useErrorHandle: false,
  145. // Whether to open back to top 是否要从头开始
  146. useOpenBackTop: true,
  147. // Is it possible to embed iframe pages 是否可以嵌入iframe页面
  148. canEmbedIFramePage: true,
  149. // Whether to delete unclosed messages and notify when switching the interface
  150. //切换界面时是否删除未关闭的消息并通知
  151. closeMessageOnSwitch: true,
  152. // Whether to cancel the http request that has been sent but not responded when switching the interface. 切换接口时是否取消已发送但未响应的http请求。
  153. // If it is enabled, I want to overwrite a single interface. Can be set in a separate interface 如果启用了,我想覆盖一个接口。可以在单独的界面中设置
  154. removeAllHttpPending: false,
  155. };
  156. export default setting;

// 模拟登录密码判断 路径:Vben Admin\vue-vben-admin\mock\sys\user.ts

  1. import { MockMethod } from 'vite-plugin-mock';
  2. import { resultError, resultSuccess, getRequestToken, requestParams } from '../_util';
  3. export function createFakeUserList() {
  4. return [
  5. {
  6. userId: '1',
  7. username: 'vben',
  8. realName: 'Vben Admin',
  9. avatar: 'https://q1.qlogo.cn/g?b=qq&nk=190848757&s=640',
  10. desc: 'manager',
  11. password: '123456',
  12. token: 'fakeToken1',
  13. homePath: '/dashboard/analysis',
  14. roles: [
  15. {
  16. roleName: 'Super Admin',
  17. value: 'super',
  18. },
  19. ],
  20. },
  21. {
  22. userId: '2',
  23. username: 'test',
  24. password: '123456',
  25. realName: 'test user',
  26. avatar: 'https://q1.qlogo.cn/g?b=qq&nk=339449197&s=640',
  27. desc: 'tester',
  28. token: 'fakeToken2',
  29. homePath: '/dashboard/workbench',
  30. roles: [
  31. {
  32. roleName: 'Tester',
  33. value: 'test',
  34. },
  35. ],
  36. },
  37. ];
  38. }
  39. const fakeCodeList: any = {
  40. '1': ['1000', '3000', '5000'],
  41. '2': ['2000', '4000', '6000'],
  42. };
  43. export default [
  44. // mock user login 模拟登录
  45. {
  46. url: '/basic-api/login',
  47. timeout: 200,
  48. method: 'post',
  49. response: ({ body }) => {
  50. const { username, password } = body;
  51. const checkUser = createFakeUserList().find(
  52. (item) => item.username === username && password === item.password,
  53. );
  54. if (!checkUser) {
  55. return resultError('Incorrect account or password!');
  56. }
  57. const { userId, username: _username, token, realName, desc, roles } = checkUser;
  58. return resultSuccess({
  59. roles,
  60. userId,
  61. username: _username,
  62. token,
  63. realName,
  64. desc,
  65. });
  66. },
  67. },
  68. {
  69. url: '/basic-api/getUserInfo',
  70. method: 'get',
  71. response: (request: requestParams) => {
  72. const token = getRequestToken(request);
  73. if (!token) return resultError('Invalid token');
  74. const checkUser = createFakeUserList().find((item) => item.token === token);
  75. if (!checkUser) {
  76. return resultError('The corresponding user information was not obtained!');
  77. }
  78. return resultSuccess(checkUser);
  79. },
  80. },
  81. {
  82. url: '/basic-api/getPermCode',
  83. timeout: 200,
  84. method: 'get',
  85. response: (request: requestParams) => {
  86. const token = getRequestToken(request);
  87. if (!token) return resultError('Invalid token');
  88. const checkUser = createFakeUserList().find((item) => item.token === token);
  89. if (!checkUser) {
  90. return resultError('Invalid token!');
  91. }
  92. const codeList = fakeCodeList[checkUser.userId];
  93. return resultSuccess(codeList);
  94. },
  95. },
  96. {
  97. url: '/basic-api/logout',
  98. timeout: 200,
  99. method: 'get',
  100. response: (request: requestParams) => {
  101. const token = getRequestToken(request);
  102. if (!token) return resultError('Invalid token');
  103. const checkUser = createFakeUserList().find((item) => item.token === token);
  104. if (!checkUser) {
  105. return resultError('Invalid token!');
  106. }
  107. return resultSuccess(undefined, { message: 'Token has been destroyed' });
  108. },
  109. },
  110. {
  111. url: '/basic-api/testRetry',
  112. statusCode: 405,
  113. method: 'get',
  114. response: () => {
  115. return resultError('Error!');
  116. },
  117. },
  118. ] as MockMethod[];

 //登录表单 

  1. <template>
  2. <LoginFormTitle v-show="getShow" class="enter-x" />
  3. <Form
  4. class="p-4 enter-x"
  5. :model="formData"
  6. :rules="getFormRules"
  7. ref="formRef"
  8. v-show="getShow"
  9. @keypress.enter="handleLogin"
  10. >
  11. <FormItem name="account" class="enter-x">
  12. <Input
  13. size="large"
  14. v-model:value="formData.account"
  15. :placeholder="t('sys.login.userName')"
  16. class="fix-auto-fill"
  17. />
  18. </FormItem>
  19. <FormItem name="password" class="enter-x">
  20. <InputPassword
  21. size="large"
  22. visibilityToggle
  23. v-model:value="formData.password"
  24. :placeholder="t('sys.login.password')"
  25. />
  26. </FormItem>
  27. <ARow class="enter-x">
  28. <ACol :span="12">
  29. <FormItem>
  30. <!-- No logic, you need to deal with it yourself -->
  31. <Checkbox v-model:checked="rememberMe" size="small">
  32. {{ t('sys.login.rememberMe') }}
  33. </Checkbox>
  34. </FormItem>
  35. </ACol>
  36. <ACol :span="12">
  37. <FormItem :style="{ 'text-align': 'right' }">
  38. <!-- No logic, you need to deal with it yourself -->
  39. <Button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)">
  40. {{ t('sys.login.forgetPassword') }}
  41. </Button>
  42. </FormItem>
  43. </ACol>
  44. </ARow>
  45. <FormItem class="enter-x">
  46. <Button type="primary" size="large" block @click="handleLogin" :loading="loading">
  47. {{ t('sys.login.loginButton') }}
  48. </Button>
  49. <!-- <Button size="large" class="mt-4 enter-x" block @click="handleRegister">
  50. {{ t('sys.login.registerButton') }}
  51. </Button> -->
  52. </FormItem>
  53. <ARow class="enter-x">
  54. <ACol :md="8" :xs="24">
  55. <Button block @click="setLoginState(LoginStateEnum.MOBILE)">
  56. {{ t('sys.login.mobileSignInFormTitle') }}
  57. </Button>
  58. </ACol>
  59. <ACol :md="8" :xs="24" class="!my-2 !md:my-0 xs:mx-0 md:mx-2">
  60. <Button block @click="setLoginState(LoginStateEnum.QR_CODE)">
  61. {{ t('sys.login.qrSignInFormTitle') }}
  62. </Button>
  63. </ACol>
  64. <ACol :md="7" :xs="24">
  65. <Button block @click="setLoginState(LoginStateEnum.REGISTER)">
  66. {{ t('sys.login.registerButton') }}
  67. </Button>
  68. </ACol>
  69. </ARow>
  70. <Divider class="enter-x">{{ t('sys.login.otherSignIn') }}</Divider>
  71. <div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`">
  72. <GithubFilled />
  73. <WechatFilled />
  74. <AlipayCircleFilled />
  75. <GoogleCircleFilled />
  76. <TwitterCircleFilled />
  77. </div>
  78. </Form>
  79. </template>
  80. <script lang="ts" setup>
  81. import { reactive, ref, unref, computed } from 'vue';
  82. import { Checkbox, Form, Input, Row, Col, Button, Divider } from 'ant-design-vue';
  83. import {
  84. GithubFilled,
  85. WechatFilled,
  86. AlipayCircleFilled,
  87. GoogleCircleFilled,
  88. TwitterCircleFilled,
  89. } from '@ant-design/icons-vue';
  90. import LoginFormTitle from './LoginFormTitle.vue';
  91. import { useI18n } from '/@/hooks/web/useI18n';
  92. import { useMessage } from '/@/hooks/web/useMessage';
  93. import { useUserStore } from '/@/store/modules/user';
  94. import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
  95. import { useDesign } from '/@/hooks/web/useDesign';
  96. //import { onKeyStroke } from '@vueuse/core';
  97. const ACol = Col;
  98. const ARow = Row;
  99. const FormItem = Form.Item;
  100. const InputPassword = Input.Password;
  101. const { t } = useI18n();
  102. const { notification, createErrorModal } = useMessage();
  103. const { prefixCls } = useDesign('login');
  104. const userStore = useUserStore();
  105. //登录方式
  106. const { setLoginState, getLoginState } = useLoginState();
  107. //注册的内容
  108. const { getFormRules } = useFormRules();
  109. //ref函数仅能监听基本类型的变化,不能监听复杂类型的变化(比如对象、数组)监听复杂类型的变化可以使用reactive函数
  110. //坑:ref和shallowRef不能一起用这样会影响视图
  111. const formRef = ref();
  112. const loading = ref(false);
  113. const rememberMe = ref(false);
  114. //模拟账号
  115. const formData = reactive({
  116. account: 'vben',
  117. password: '123456',
  118. });
  119. const { validForm } = useFormValid(formRef);
  120. //onKeyStroke('Enter', handleLogin);
  121. const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN);
  122. //登录的条件判断
  123. async function handleLogin() {
  124. const data = await validForm();
  125. if (!data) return;
  126. //try | catch | finally 语句的用法try包裹的是密码判断的内容如果try里的密码错误就会抛出catch里的错误内容
  127. try {
  128. loading.value = true;
  129. const userInfo = await userStore.login({
  130. password: data.password,
  131. username: data.account,
  132. mode: 'none', //不要默认的错误提示
  133. });
  134. if (userInfo) {
  135. notification.success({
  136. message: t('sys.login.loginSuccessTitle'),
  137. description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`,
  138. duration: 3,
  139. });
  140. }
  141. } catch (error) {
  142. createErrorModal({
  143. title: t('sys.api.errorTip'),
  144. content: (error as unknown as Error).message || t('sys.api.networkExceptionMsg'),
  145. getContainer: () => document.body.querySelector(`.${prefixCls}`) || document.body,
  146. });
  147. } finally {
  148. loading.value = false;
  149. }
  150. }
  151. </script>

 //关于主页的一些简单的配置

  1. interface GroupItem {
  2. title: string;
  3. icon: string;
  4. color: string;
  5. desc: string;
  6. date: string;
  7. group: string;
  8. }
  9. interface NavItem {
  10. title: string;
  11. icon: string;
  12. color: string;
  13. }
  14. interface DynamicInfoItem {
  15. avatar: string;
  16. name: string;
  17. date: string;
  18. desc: string;
  19. }
  20. export const navItems: NavItem[] = [
  21. {
  22. title: '首页',
  23. icon: 'ion:home-outline',
  24. color: '#1fdaca',
  25. },
  26. // {
  27. // title: '仪表盘',
  28. // icon: 'ion:grid-outline',
  29. // color: '#bf0c2c',
  30. // },
  31. // {
  32. // title: '组件',
  33. // icon: 'ion:layers-outline',
  34. // color: '#e18525',
  35. // },
  36. // {
  37. // title: '系统管理',
  38. // icon: 'ion:settings-outline',
  39. // color: '#3fb27f',
  40. // },
  41. // {
  42. // title: '权限管理',
  43. // icon: 'ion:key-outline',
  44. // color: '#4daf1bc9',
  45. // },
  46. // {
  47. // title: '图表',
  48. // icon: 'ion:bar-chart-outline',
  49. // color: '#00d8ff',
  50. // },
  51. ];
  52. export const dynamicInfoItems: DynamicInfoItem[] = [
  53. {
  54. avatar: 'dynamic-avatar-1|svg',
  55. name: '威廉',
  56. date: '刚刚',
  57. desc: `在 <a>开源组</a> 创建了项目 <a>Vue</a>`,
  58. },
  59. {
  60. avatar: 'dynamic-avatar-2|svg',
  61. name: '艾文',
  62. date: '1个小时前',
  63. desc: `关注了 <a>威廉</a> `,
  64. },
  65. {
  66. avatar: 'dynamic-avatar-3|svg',
  67. name: '克里斯',
  68. date: '1天前',
  69. desc: `发布了 <a>个人动态</a> `,
  70. },
  71. {
  72. avatar: 'dynamic-avatar-4|svg',
  73. name: 'Vben',
  74. date: '2天前',
  75. desc: `发表文章 <a>如何编写一个Vite插件</a> `,
  76. },
  77. {
  78. avatar: 'dynamic-avatar-5|svg',
  79. name: '皮特',
  80. date: '3天前',
  81. desc: `回复了 <a>杰克</a> 的问题 <a>如何进行项目优化?</a>`,
  82. },
  83. {
  84. avatar: 'dynamic-avatar-6|svg',
  85. name: '杰克',
  86. date: '1周前',
  87. desc: `关闭了问题 <a>如何运行项目</a> `,
  88. },
  89. {
  90. avatar: 'dynamic-avatar-1|svg',
  91. name: '威廉',
  92. date: '1周前',
  93. desc: `发布了 <a>个人动态</a> `,
  94. },
  95. {
  96. avatar: 'dynamic-avatar-1|svg',
  97. name: '威廉',
  98. date: '2021-04-01 20:00',
  99. desc: `推送了代码到 <a>Github</a>`,
  100. },
  101. ];
  102. export const groupItems: GroupItem[] = [
  103. {
  104. title: 'Github',
  105. icon: 'carbon:logo-github',
  106. color: '',
  107. desc: '不要等待机会,而要创造机会。',
  108. group: '开源组',
  109. date: '2021-04-01',
  110. },
  111. {
  112. title: 'Vue',
  113. icon: 'ion:logo-vue',
  114. color: '#3fb27f',
  115. desc: '现在的你决定将来的你。',
  116. group: '算法组',
  117. date: '2021-04-01',
  118. },
  119. {
  120. title: 'Html5',
  121. icon: 'ion:logo-html5',
  122. color: '#e18525',
  123. desc: '没有什么才能比努力更重要。',
  124. group: '上班摸鱼',
  125. date: '2021-04-01',
  126. },
  127. {
  128. title: 'Angular',
  129. icon: 'ion:logo-angular',
  130. color: '#bf0c2c',
  131. desc: '热情和欲望可以突破一切难关。',
  132. group: 'UI',
  133. date: '2021-04-01',
  134. },
  135. {
  136. title: 'React',
  137. icon: 'bx:bxl-react',
  138. color: '#00d8ff',
  139. desc: '健康的身体是实目标的基石。',
  140. group: '技术牛',
  141. date: '2021-04-01',
  142. },
  143. {
  144. title: 'Js',
  145. icon: 'ion:logo-javascript',
  146. color: '#4daf1bc9',
  147. desc: '路是走出来的,而不是空想出来的。',
  148. group: '架构组',
  149. date: '2021-04-01',
  150. },
  151. ];

//关于抽奖导航

  1. <template>
  2. <Card title="快捷导航" v-bind="$attrs">
  3. <CardGrid v-for="item in navItems" :key="item" >
  4. <span class="flex flex-col items-center">
  5. <!--图标-->
  6. <Icon :icon="item.icon" :color="item.color" size="20" />
  7. <span class="text-md mt-2">{{ item.title }}</span>
  8. </span>
  9. </CardGrid>
  10. </Card>
  11. </template>
  12. <script lang="ts" setup>
  13. import { Card } from 'ant-design-vue';
  14. import { navItems } from './data';
  15. import { Icon } from '/@/components/Icon';
  16. //navItems是上面data里的。
  17. const CardGrid = Card.Grid;
  18. </script>

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发