فهرست منبع

feat: 初始化项目

qinlaiqiao 3 سال پیش
کامیت
8c0ca47ac2

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
+build
+.idea

+ 12 - 0
bootstrap.js

@@ -0,0 +1,12 @@
+const { Framework } = require('@midwayjs/koa');
+const { Bootstrap } = require('@midwayjs/bootstrap');
+
+const web = new Framework().configure({
+  port: 7001,
+});
+
+Bootstrap.load(web)
+  .run()
+  .then(() => {
+    console.log('Your application is running at http://localhost:7001');
+  });

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8"/>
+    <link rel="icon" type="image/svg+xml" href="src/favicon.svg"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+    <title>Vite App</title>
+</head>
+<body>
+<div id="app"></div>
+<script type="module" src="/src/main.ts"></script>
+</body>
+</html>

+ 3 - 0
jest.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  preset: '@midwayjs/hooks',
+}

+ 11 - 0
midway.config.ts

@@ -0,0 +1,11 @@
+import { defineConfig } from '@midwayjs/hooks';
+
+export default defineConfig({
+  source: './src/apis',
+  routes: [
+    {
+      baseDir: 'controller',
+      basePath: '/api',
+    },
+  ],
+});

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 10593 - 0
package-lock.json


+ 42 - 0
package.json

@@ -0,0 +1,42 @@
+{
+  "name": "Platform",
+  "version": "0.1.1",
+  "private": true,
+  "scripts": {
+    "start": "node bootstrap",
+    "dev": "vite",
+    "build": "npm run build:client && npm run build:server",
+    "build:client": "tsc && vite build",
+    "build:server": "mw build",
+    "serve": "vite preview",
+    "test": "jest"
+  },
+  "dependencies": {
+    "@midwayjs/hooks": "^2.2.2",
+    "@midwayjs/koa": "^2.11.0",
+    "animate.css": "^4.1.1",
+    "animejs": "^3.2.1",
+    "element-plus": "^1.2.0-beta.1",
+    "koa-bodyparser": "^4.3.0",
+    "vue": "^3.2.16",
+    "vue-router": "^4.0.12"
+  },
+  "devDependencies": {
+    "@midwayjs/cli": "^1.2.71",
+    "@midwayjs/cli-plugin-build": "^1.2.70",
+    "@midwayjs/hooks-testing-library": "^2.2.2",
+    "@midwayjs/vite-plugin-hooks": "^2.2.3",
+    "@types/animejs": "^3.1.4",
+    "@types/jest": "^26.0.23",
+    "@types/koa-bodyparser": "^4.3.1",
+    "@vitejs/plugin-vue": "^1.9.3",
+    "autoprefixer": "^10.4.0",
+    "jest": "^26.6.3",
+    "postcss": "^8.3.11",
+    "sass": "^1.43.4",
+    "tailwindcss": "^2.2.19",
+    "typescript": "^4.4.3",
+    "vite": "^2.6.4",
+    "vue-tsc": "^0.3.0"
+  }
+}

+ 6 - 0
postcss.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  },
+}

+ 95 - 0
readme.md

@@ -0,0 +1,95 @@
+## Platform ⚡
+
+本项目基于 midway hooks ,大多数情况下遵循了官方建议。
+
+但考虑到实际项目的复杂性与工作量,在某些代码的组织上沿用了之前项目的习惯。
+
+具体情况如下:
+
+### 目录结构
+
+```
+.
+├── bootstrap.js // 应用启动
+├── jest.config.js // 单元测试
+├── midway.config.ts // Midway 配置文件
+├── src
+│   ├── apis // 后端目录
+│   │   ├── configuration.ts // Midway Hooks configuration
+│   │   └── lambda // Api 目录
+│   │       ├── index.test.ts // Api 测试文件
+│   │       └── index.ts // Api 文件
+│   └── main.ts // 前端框架入口文件
+├── tsconfig.json
+└── vite.config.ts
+```
+
+#### 相关目录说明
+
+##### apis
+
+apis 目录用于存放后端代码。
+
+这里没有使用 midway hooks 官方建议的那样,将目录简单的划分为 `lambda(路由)` 和 `hooks(逻辑处理)`,而是沿用了大司空后端的结构。
+
+一些通用的结构,如 utils 目录已经被移到 `src/utils` 下。
+
+随着项目的迭代开发,可能会在各级目录中增加多个子目录,这是允许的。 但要保持一个原则:
+
+目录结构尽可能精简。
+
+想象一下,某个目录下有几十个子目录是什么体验?实际上,很多子目录的创建,都是“临时起意”,比如 `types` 和 `interface`。
+
+再有,新建的目录在其父级目录下,要有明确的语义,比如 `utils/common`,结合其他子目录,可以很容易的猜到其含义,但是你能猜到 `src/common` 是什么含义吗?没人知道。
+
+##### utils
+
+utils 目录用于存放相关工具方法,并拆分为了 3 个子目录:
+
+- common 用于存在前后端都可能会用到的一些方法,比如封装 http 请求的函数
+- backend 只存在后端会用到的方法,比如数据库相关的函数
+- frontend 之存放前端会用到的方法,比如自行封装的 Alert 函数
+
+### 相关技术
+
+#### Vite
+
+使用 Vite 进行打包,速度快。
+
+#### midway 函数式和一体化
+
+文档:[Midway Hooks - 介绍](https://www.midwayjs.org/docs/hooks_intro)
+
+#### CSS
+
+本项目使用 scss。
+
+本打算使用原生 css 变量 + PostCSS ,这也是 Vite 推荐的方式。但是考虑到大家的学习成本和项目进度,依然选择了熟悉的 scss。
+
+#### 别名
+
+1. @ : src
+
+2. controller : src/apis/controller
+
+3. service : src/apis/service
+
+### 相关命令
+
+#### 开发 server
+
+```bash
+$ npm run dev
+```
+
+#### 构建
+
+```bash
+$ npm run build
+```
+
+### 生产环境运行
+
+```bash
+$ node bootstrap.js
+```

+ 12 - 0
src/App.vue

@@ -0,0 +1,12 @@
+<script setup lang="ts">
+</script>
+
+<template>
+  <router-view/>
+</template>
+
+<style lang="scss">
+#app {
+  @apply w-full h-full
+}
+</style>

+ 10 - 0
src/apis/configuration.ts

@@ -0,0 +1,10 @@
+import {hooks, createConfiguration} from '@midwayjs/hooks';
+import bodyParser from 'koa-bodyparser';
+
+export default createConfiguration({
+    imports: [
+        hooks({
+            middleware: [bodyParser()],
+        }),
+    ],
+});

+ 38 - 0
src/apis/controller/index.test.ts

@@ -0,0 +1,38 @@
+import { createApp, HooksApplication } from '@midwayjs/hooks-testing-library';
+import api, { post } from '.';
+
+describe('test new features', () => {
+  let app: HooksApplication;
+  beforeAll(async () => {
+    app = await createApp();
+  });
+
+  afterAll(async () => {
+    await app.close();
+  });
+
+  it('runFunction', async () => {
+    expect(await app.runFunction(api)).toMatchInlineSnapshot(`
+      Object {
+        "message": "Hello World",
+        "method": "GET",
+      }
+    `);
+    expect(await app.runFunction(post, 'Jake')).toMatchInlineSnapshot(`
+      Object {
+        "method": "POST",
+        "name": "Jake",
+      }
+    `);
+  });
+
+  it('request', async () => {
+    const response = await app.request(api).expect(200);
+    expect(response.body).toMatchInlineSnapshot(`
+      Object {
+        "message": "Hello World",
+        "method": "GET",
+      }
+    `);
+  });
+});

+ 17 - 0
src/apis/controller/index.ts

@@ -0,0 +1,17 @@
+import { useContext } from '@midwayjs/hooks';
+import { Context } from '@midwayjs/koa';
+
+function useKoaContext() {
+  return useContext<Context>();
+}
+
+export default async () => {
+  return {
+    message: 'Hello World',
+    method: useKoaContext().method,
+  };
+};
+
+export const post = async (name: string) => {
+  return { method: 'POST', name };
+};

+ 8 - 0
src/env.d.ts

@@ -0,0 +1,8 @@
+/// <reference types="vite/client" />
+
+declare module '*.vue' {
+  import { DefineComponent } from 'vue'
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 15 - 0
src/favicon.svg


+ 11 - 0
src/main.ts

@@ -0,0 +1,11 @@
+import {createApp} from 'vue'
+import App from './App.vue'
+import ElementPlus from 'element-plus'
+import zhCn from 'element-plus/es/locale/lang/zh-cn'
+import '@/styles/index.scss';
+import router from './router';
+
+const app = createApp(App)
+app.use(ElementPlus, {size: 'mini', locale: zhCn,})
+app.use(router)
+app.mount('#app')

+ 30 - 0
src/styles/_element_plus.scss

@@ -0,0 +1,30 @@
+/* 只需要覆盖你需要的变量 */
+@forward "element-plus/theme-chalk/src/common/var.scss" with (
+  $colors: (
+    'white': #ffffff,
+    'black': #000000,
+    "primary": (
+      "base": $primary,
+    ),
+    'success': (
+      'base': $success,
+    ),
+    'warning': (
+      'base': $warning,
+    ),
+    'danger': (
+      'base': $danger,
+    ),
+    'error': (
+      'base': $danger,
+    ),
+    'info': (
+      'base': $info,
+    ),
+  ),
+);
+
+@import "element-plus/theme-chalk/src/index.scss";
+
+// ===== 下面写覆盖的样式
+

+ 21 - 0
src/styles/_main.scss

@@ -0,0 +1,21 @@
+html {
+  height: 100%;
+  width: 100%;
+  font-size: 12px;
+
+  body {
+    width: 100%;
+    height: 100%;
+    font-family: "微软雅黑", "Microsoft YaHei", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
+    -webkit-font-smoothing: antialiased; /*chrome、safari*/
+    -moz-osx-font-smoothing: grayscale; /*firefox*/
+    -webkit-text-size-adjust: none;
+    line-height: 1.5715;
+  }
+}
+
+/* tailwindcss */
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+

+ 13 - 0
src/styles/_mixin.scss

@@ -0,0 +1,13 @@
+// 函数示例
+@function px2rem($px) {
+  $rem: 14px;
+  @return ($px/$rem) + rem;
+}
+
+// mixin 示例
+@mixin center {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+}

+ 53 - 0
src/styles/_reset.scss

@@ -0,0 +1,53 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+   v2.0 | 20110126
+   License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  font-size: 100%;
+  font: inherit;
+}
+
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+  display: block;
+}
+
+body {
+  line-height: 1;
+}
+
+ol, ul {
+  list-style: none;
+}
+
+blockquote, q {
+  quotes: none;
+}
+
+blockquote:before, blockquote::after,
+q:before, q::after {
+  content: '';
+  content: none;
+}
+
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}

+ 44 - 0
src/styles/_variables.scss

@@ -0,0 +1,44 @@
+// 小屏幕
+$small-screen: 1140px;
+
+// 中等屏幕
+$medium-screen: 1440px;
+
+// 大屏幕
+$large-screen: 1680px;
+
+// 超大屏
+$x-large-screen: 1840px;
+
+/* 颜色 */
+$primary: #37599f;
+$primary-deep: #2c4c8c;
+$success: #15a809;
+$success-deep: #159e09;
+$warning: #d08808;
+$warning-deep: #ca8707;
+$danger: #eb1e1e;
+$danger-deep: #d51d1d;
+$info: #232a2d;
+$backdrop: #fbfaf9;
+$text-primary: #535a5d;
+
+/* 导出变量(css in js) 变量名为cssVars */
+:export {
+  smallScreen: $small-screen;
+  mediumScreen: $medium-screen;
+  largeScreen: $large-screen;
+
+  /* 一些主要颜色变量 https://smartcost.yuque.com/kigzpm/gsx7gg/nasbuh/edit */
+  primary: $primary;
+  primaryDeep: $primary-deep;
+  success: $success;
+  successDeep: $success-deep;
+  warning: $warning;
+  warningDeep: $warning-deep;
+  danger: $danger;
+  dangerDeep: $danger-deep;
+  info: $info;
+  backdrop: $backdrop;
+  $textPrimary: $text-primary;
+}

+ 9 - 0
src/styles/index.scss

@@ -0,0 +1,9 @@
+@import "reset.scss";
+@import "main.scss";
+@import "element_plus";
+@import 'animate.css';
+
+// iconfont,新增图标需要更新此url
+@import url("//at.alicdn.com/t/font_2835708_bg21nztdnq5.css");
+
+

+ 31 - 0
src/views/HelloWorld.vue

@@ -0,0 +1,31 @@
+<script setup lang="ts">
+import {ref} from 'vue'
+
+defineProps<{ msg: string }>()
+
+const count = ref(0)
+
+</script>
+
+<template>
+  <h1>{{ msg }}</h1>
+  <el-button type="primary">这是一个按钮</el-button>
+</template>
+
+<style scoped>
+a {
+  color: #42b983;
+}
+
+label {
+  margin: 0 0.5em;
+  font-weight: bold;
+}
+
+code {
+  background-color: #eee;
+  padding: 2px 4px;
+  border-radius: 4px;
+  color: #304455;
+}
+</style>

+ 31 - 0
tailwind.config.js

@@ -0,0 +1,31 @@
+module.exports = {
+    purge: [],
+    darkMode: false, // or 'media' or 'class'
+    theme: {
+        extend: {
+            zIndex: {
+                '-10': '-10',
+            }
+        },
+        screens: {
+            'sm': '1140px',
+            // => @media (min-width: 1140px) { ... }
+
+            'md': '1440px',
+            // => @media (min-width: 1440px) { ... }
+
+            'lg': '1680px',
+            // => @media (min-width: 1680px) { ... }
+
+            'xl': '1840px',
+            // => @media (min-width: 1840px) { ... }
+
+            '2xl': '1920px',
+            // => @media (min-width: 1920px) { ... }
+        },
+    },
+    variants: {
+        extend: {},
+    },
+    plugins: [],
+}

+ 39 - 0
tsconfig.json

@@ -0,0 +1,39 @@
+{
+  "compilerOptions": {
+    "target": "ESNext",
+    "lib": [
+      "DOM",
+      "DOM.Iterable",
+      "ESNext"
+    ],
+    "allowJs": false,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "strict": true,
+    "skipLibCheck": true,
+    "forceConsistentCasingInFileNames": true,
+    "module": "ESNext",
+    "moduleResolution": "Node",
+    "resolveJsonModule": true,
+    "noEmit": true,
+    "jsx": "preserve",
+    "sourceMap": true,
+    "experimentalDecorators": true,
+    "emitDecoratorMetadata": true,
+    "types": [
+      "vite/client",
+      "jest"
+    ]
+  },
+  "include": [
+    "src/**/*.ts",
+    "src/**/*.d.ts",
+    "src/**/*.tsx",
+    "src/**/*.vue"
+  ],
+  "ts-node": {
+    "compilerOptions": {
+      "module": "commonjs"
+    }
+  }
+}

+ 28 - 0
vite.config.ts

@@ -0,0 +1,28 @@
+import {defineConfig} from 'vite';
+import vue from '@vitejs/plugin-vue'
+import hooks from '@midwayjs/vite-plugin-hooks';
+
+const {resolve} = require('path')
+
+// https://vitejs.dev/config/
+export default defineConfig({
+    plugins: [hooks(), vue()],
+    resolve: {
+        alias: {
+            '@': resolve(__dirname, 'src'),
+            'controller': resolve(__dirname, 'src/apis/controller'),
+            'service': resolve(__dirname, 'src/apis/service'),
+            'utils': resolve(__dirname, 'src/utils')
+        }
+    },
+    css: {
+        preprocessorOptions: {
+            scss: {
+                additionalData: `
+                @import "@/styles/_variables.scss";
+                @import "@/styles/_mixin.scss";
+                `
+            }
+        }
+    }
+});