├── .env.development ├── .env.production ├── .gitignore ├── .prettierrc ├── .vscode └── extensions.json ├── README.md ├── index.html ├── package.json ├── postcss.config.js ├── public └── vite.svg ├── src ├── App.vue ├── api │ ├── category.js │ ├── list.js │ ├── pay.js │ └── sys.js ├── assets │ ├── icons │ │ ├── back.svg │ │ ├── calendar.svg │ │ ├── change-header-image.svg │ │ ├── close.svg │ │ ├── comment.svg │ │ ├── countdown.svg │ │ ├── delete.svg │ │ ├── down-arrow.svg │ │ ├── download.svg │ │ ├── error.svg │ │ ├── feedback.svg │ │ ├── fine-loading.svg │ │ ├── fold.svg │ │ ├── full.svg │ │ ├── guide.svg │ │ ├── hamburger.svg │ │ ├── heart.svg │ │ ├── home.svg │ │ ├── infinite-load.svg │ │ ├── input-delete.svg │ │ ├── like.svg │ │ ├── loading.svg │ │ ├── logout.svg │ │ ├── pay-fail.svg │ │ ├── pay-success.svg │ │ ├── profile.svg │ │ ├── qq.svg │ │ ├── read.svg │ │ ├── refresh.svg │ │ ├── remind.svg │ │ ├── search.svg │ │ ├── setting.svg │ │ ├── share.svg │ │ ├── success.svg │ │ ├── theme-dark.svg │ │ ├── theme-light.svg │ │ ├── theme-system.svg │ │ ├── theme.svg │ │ ├── unfold.svg │ │ ├── vip-profile.svg │ │ ├── vip.svg │ │ ├── warn.svg │ │ ├── wei-bo.svg │ │ ├── wei-xin.svg │ │ ├── wexin.svg │ │ └── zhi-fu-bao.svg │ ├── images │ │ └── alipay.png │ └── vue.svg ├── components │ └── HelloWorld.vue ├── constants │ └── index.js ├── directives │ ├── index.js │ └── modules │ │ └── lazy.js ├── libs │ ├── button │ │ └── index.vue │ ├── confirm │ │ ├── index.js │ │ └── index.vue │ ├── count-down │ │ ├── index.vue │ │ └── utils.js │ ├── dialog │ │ └── index.vue │ ├── index.js │ ├── infinite-list │ │ └── index.vue │ ├── input │ │ └── index.vue │ ├── message │ │ ├── index.js │ │ └── index.vue │ ├── navbar │ │ └── index.vue │ ├── popover │ │ └── index.vue │ ├── popup │ │ └── index.vue │ ├── search │ │ └── index.vue │ ├── svg-icon │ │ └── index.vue │ ├── transition-router-view │ │ └── index.vue │ ├── trigger-menu-item │ │ └── index.vue │ ├── trigger-menu │ │ └── index.vue │ └── waterfall │ │ ├── index.vue │ │ └── utils.js ├── main.js ├── permission.js ├── router │ ├── index.js │ └── modules │ │ ├── mobile-routes.js │ │ └── pc-routes.js ├── store │ ├── getters.js │ ├── index.js │ └── modules │ │ ├── app.js │ │ ├── category.js │ │ ├── search.js │ │ ├── theme.js │ │ └── user.js ├── style.css ├── styles │ └── index.scss ├── utils │ ├── color.js │ ├── flexible.js │ ├── index.js │ ├── request.js │ ├── sts.js │ └── theme.js ├── vendor │ └── SliderCaptcha │ │ ├── longbow.slidercaptcha.min.js │ │ └── slidercaptcha.min.css └── views │ ├── layout │ ├── components │ │ ├── floating │ │ │ ├── index.vue │ │ │ └── steps.js │ │ ├── header │ │ │ ├── header-my.vue │ │ │ ├── header-search │ │ │ │ ├── hint.vue │ │ │ │ ├── history.vue │ │ │ │ ├── hot.vue │ │ │ │ └── index.vue │ │ │ ├── header-theme.vue │ │ │ └── index.vue │ │ └── main │ │ │ └── index.vue │ └── index.vue │ ├── login │ ├── components │ │ ├── header.vue │ │ ├── qq-login.vue │ │ ├── slider-captcha.vue │ │ └── wechat-login.vue │ ├── index.vue │ └── validate.js │ ├── main │ ├── components │ │ ├── list │ │ │ ├── index.vue │ │ │ └── item.vue │ │ ├── menu │ │ │ └── index.vue │ │ └── navigation │ │ │ ├── index.vue │ │ │ ├── mobile │ │ │ └── index.vue │ │ │ └── pc │ │ │ └── index.vue │ └── index.vue │ ├── member │ ├── components │ │ ├── pay-menu-item.vue │ │ └── payment │ │ │ ├── discounts.vue │ │ │ ├── index.vue │ │ │ ├── mobile-payment │ │ │ ├── index.vue │ │ │ └── mobile-pay-select.vue │ │ │ └── pc-payment │ │ │ └── index.vue │ └── index.vue │ ├── profile │ ├── components │ │ └── change-avatar.vue │ └── index.vue │ └── register │ └── index.vue ├── tailwind.config.js ├── vite.config.js └── yarn.lock /.env.development: -------------------------------------------------------------------------------- 1 | VITE_BASE_API = "/api" -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VITE_BASE_API = "/prod-api" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "none" 5 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
17 | Edit
18 | components/HelloWorld.vue
to test HMR
19 |
23 | Check out 24 | create-vue, the official Vue + Vite starter 27 |
28 |29 | Install 30 | Volar 31 | in your IDE for a better DX 32 |
33 |Click on the Vite and Vue logos to learn more
34 | 35 | 36 | 41 | -------------------------------------------------------------------------------- /src/constants/index.js: -------------------------------------------------------------------------------- 1 | export const PC_DEVICE_WIDTH = 1280 2 | 3 | // 全部分类 4 | export const ALL_CATEGORY = { id: 'all', name: '全部' } 5 | 6 | // 暗黑主题 7 | export const THEME_DARK = 'dark' 8 | // 浅色主题 9 | export const THEME_LIGHT = 'light' 10 | // 系统主题 11 | export const THEME_SYSTEM = 'system' 12 | 13 | // 初始 category 数据 14 | export const CATEGORY_NOMAR_DATA = [ 15 | ALL_CATEGORY, 16 | { id: 'web_app_icon', name: 'UI/UX' }, 17 | { id: 'design', name: '平面' }, 18 | { id: 'illustration', name: '插画/漫画' }, 19 | { id: 'photography', name: '摄影' }, 20 | { id: 'games', name: '游戏' }, 21 | { id: 'anime', name: '动漫' }, 22 | { 23 | id: 'industrial_design', 24 | name: '工业设计' 25 | }, 26 | { 27 | id: 'industrial_design', 28 | name: '建筑设计' 29 | }, 30 | { 31 | id: 'industrial_design', 32 | name: '人文艺术' 33 | }, 34 | { 35 | id: 'industrial_design', 36 | name: '家居/家装' 37 | } 38 | ] 39 | 40 | // STS 上传数据 41 | export const REGION = 'oss-cn-beijing' 42 | export const BUCKET = 'imooc-front' 43 | -------------------------------------------------------------------------------- /src/directives/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | install(app) { 3 | // 获取到所有指令 4 | const modules = import.meta.globEager('./modules/*.js') 5 | for (let [key, value] of Object.entries(modules)) { 6 | const directiveName = key.replace('./modules/', '').split('.')[0] 7 | app.directive(directiveName, value.default) 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/directives/modules/lazy.js: -------------------------------------------------------------------------------- 1 | import { useIntersectionObserver } from '@vueuse/core' 2 | 3 | export default { 4 | mounted(el) { 5 | // 保存图片路径 6 | const imgSrc = el.getAttribute('src') 7 | // 将图片src置空 8 | el.setAttribute('src', '') 9 | 10 | // 监听图片的可见 11 | const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => { 12 | if (isIntersecting) { 13 | el.setAttribute('src', imgSrc) 14 | // 停止监听 15 | stop() 16 | } 17 | }) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/libs/button/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 29 | 30 | 31 | 61 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/libs/confirm/index.js: -------------------------------------------------------------------------------- 1 | import { h, render } from 'vue' 2 | import Confirm from './index.vue' 3 | 4 | export function createConfirm({ 5 | title, 6 | content, 7 | cancelText = '取消', 8 | confirmText = '确定' 9 | }) { 10 | return new Promise((resolve, reject) => { 11 | // 暂时性死区 12 | // const confirmInstance = createApp(Confirm, { 13 | // title, 14 | // content, 15 | // cancelText, 16 | // confirmText, 17 | // closeAfter, 18 | // handleConfirmClick, 19 | // handleCancelClick 20 | // }) 21 | // const mountNode = document.createElement('div') 22 | // document.body.appendChild(mountNode) 23 | // confirmInstance.mount(mountNode) 24 | 25 | /** 26 | * 移除confirm 27 | */ 28 | const closeAfter = () => { 29 | // const confirmId = document.getElementById('confirm') 30 | // confirmId && document.body.removeChild(confirmId) 31 | 32 | // 2. render 33 | render(null, document.body) 34 | } 35 | 36 | /** 37 | * 点击确定按钮,回调 38 | */ 39 | const handleConfirmClick = resolve 40 | 41 | /** 42 | * 点击取消按钮,回调 43 | */ 44 | const handleCancelClick = reject 45 | 46 | // 1. vnode 生成vnode,并传入props 47 | const vnode = h(Confirm, { 48 | title, 49 | content, 50 | cancelText, 51 | confirmText, 52 | closeAfter, 53 | handleConfirmClick, 54 | handleCancelClick 55 | }) 56 | // 2. render 渲染组件到body中 57 | render(vnode, document.body) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /src/libs/confirm/index.vue: -------------------------------------------------------------------------------- 1 | 2 |5 | {{ showTime }} 6 |
7 |15 | 亲,已经没有更多数据啦! 16 |
17 |
12 |
20 | # {{ hotThemeData.big.title }} 21 |
22 |40 | # {{ item.title }} 41 |
42 |56 | {{ data.title }} 57 |
58 | 59 |21 | {{ data.title }} 22 |
23 | 24 |32 | ¥ 33 | {{ data.price }} 34 |
35 | 36 |¥{{ data.oldPrice }}
37 | 38 |
12 | 限时特惠 | 距离优惠结束仅剩
13 |
11 | 券后合计:¥9 14 |
15 |优惠券:限时立减 ¥10
16 |支付宝
13 |9 | 10 | 支付金额: 11 | 12 | ¥ 13 | 19 14 |
15 | 16 |支付宝
24 |22 | 升级精选VIP,畅想所有内容 23 |
24 | 35 |{{ currentPayData.desc }}
36 | 37 |