create-miniprogram-scaffold 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/bin/cli.js +1 -1
  2. package/package.json +3 -2
  3. package/templates/miniprogram/assets/tabbar/discover-active.png +0 -0
  4. package/templates/miniprogram/assets/tabbar/discover.png +0 -0
  5. package/templates/miniprogram/assets/tabbar/home-active.png +0 -0
  6. package/templates/miniprogram/assets/tabbar/home.png +0 -0
  7. package/templates/miniprogram/assets/tabbar/mine-active.png +0 -0
  8. package/templates/miniprogram/assets/tabbar/mine.png +0 -0
  9. package/templates/miniprogram/babel.config.js +10 -0
  10. package/templates/miniprogram/config/dev.ts +2 -0
  11. package/templates/miniprogram/config/index.ts +17 -0
  12. package/templates/miniprogram/config/prod.ts +2 -0
  13. package/templates/miniprogram/package.json +16 -0
  14. package/templates/miniprogram/project.config.json +31 -0
  15. package/templates/miniprogram/scripts/generate-tabbar-icons.js +9 -0
  16. package/templates/miniprogram/src/app.scss +9 -0
  17. package/templates/miniprogram/src/app.tsx +16 -0
  18. package/templates/miniprogram/src/layouts/FullscreenLayout/index.scss +2 -0
  19. package/templates/miniprogram/src/layouts/FullscreenLayout/index.tsx +6 -0
  20. package/templates/miniprogram/src/layouts/HeaderTabBarLayout/index.scss +11 -0
  21. package/templates/miniprogram/src/layouts/HeaderTabBarLayout/index.tsx +20 -0
  22. package/templates/miniprogram/src/layouts/README.md +7 -0
  23. package/templates/miniprogram/src/layouts/TabBarLayout/index.scss +1 -0
  24. package/templates/miniprogram/src/layouts/TabBarLayout/index.tsx +6 -0
  25. package/templates/miniprogram/src/layouts/index.ts +3 -0
  26. package/templates/miniprogram/src/pages/discover/index.config.ts +1 -0
  27. package/templates/miniprogram/src/pages/discover/index.scss +4 -0
  28. package/templates/miniprogram/src/pages/discover/index.tsx +6 -0
  29. package/templates/miniprogram/src/pages/home/index.config.ts +1 -0
  30. package/templates/miniprogram/src/pages/home/index.scss +6 -0
  31. package/templates/miniprogram/src/pages/home/index.tsx +17 -0
  32. package/templates/miniprogram/src/pages/mine/index.config.ts +1 -0
  33. package/templates/miniprogram/src/pages/mine/index.scss +4 -0
  34. package/templates/miniprogram/src/pages/mine/index.tsx +6 -0
  35. package/templates/miniprogram/src/pages/splash/index.config.ts +1 -0
  36. package/templates/miniprogram/src/pages/splash/index.scss +11 -0
  37. package/templates/miniprogram/src/pages/splash/index.tsx +15 -0
  38. package/templates/miniprogram/src/styles/variables.scss +41 -0
  39. package/templates/miniprogram/tsconfig.json +31 -0
  40. package/templates/miniprogram/types/global.d.ts +18 -0
  41. package/templates/server/.env.example +13 -0
  42. package/templates/server/Makefile +76 -0
  43. package/templates/server/README.md +134 -0
  44. package/templates/server/go.mod +25 -0
  45. package/templates/server/main.go +126 -0
package/bin/cli.js CHANGED
@@ -27,7 +27,7 @@ program.name('create-mini-scaffold').description('Create Taro + Go mini program
27
27
  ]}, { type: 'input', name: 'port', message: 'Port?', default: cfg.port } ]); Object.assign(cfg, a); }
28
28
  if (!['header-tabbar','tabbar','fullscreen','all'].includes(cfg.layout)) { console.error(chalk.red('Invalid layout')); process.exit(1); }
29
29
  console.log(chalk.blue('\nCreating...'));
30
- const tDir = path.join(__dirname, '..', '..', 'templates');
30
+ const tDir = path.join(__dirname, '..', 'templates');
31
31
  const mDir = path.join(dir, 'miniprogram');
32
32
  await fs.ensureDir(mDir); await fs.ensureDir(path.join(dir, 'server'));
33
33
  // Copy miniprogram (skip pages)
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "create-miniprogram-scaffold",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Create Taro + Go mini program projects",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/hk029/miniprogram-scaffold.git"
8
8
  },
9
9
  "main": "index.js",
10
+ "files": ["bin", "templates"],
10
11
  "bin": {
11
- "create-mini-scaffold": "./bin/cli.js"
12
+ "create-miniprogram-scaffold": "./bin/cli.js"
12
13
  },
13
14
  "scripts": {
14
15
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -0,0 +1,10 @@
1
+ // babel-preset-taro 更多选项和默认值:
2
+ // https://github.com/nicholasxjy/taro-cli/blob/master/packages/babel-preset-taro/README.md
3
+ module.exports = {
4
+ presets: [
5
+ ['taro', {
6
+ framework: 'react',
7
+ ts: true
8
+ }]
9
+ ]
10
+ }
@@ -0,0 +1,2 @@
1
+ import type { UserConfigExport } from '@tarojs/cli'
2
+ export default { logger: { quiet: false, stats: true }, mini: {} } satisfies UserConfigExport
@@ -0,0 +1,17 @@
1
+ import { defineConfig, type UserConfigExport } from '@tarojs/cli'
2
+ import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'
3
+ import devConfig from './dev'
4
+ import prodConfig from './prod'
5
+ export default defineConfig(async (merge) => {
6
+ const base: UserConfigExport = {
7
+ projectName: 'mini-scaffold', designWidth() { return 750 },
8
+ deviceRatio: { 640: 2.34/2, 750: 1, 375: 2, 828: 1.81/2 },
9
+ sourceRoot: 'src', outputRoot: 'dist', plugins: [], framework: 'react', compiler: 'webpack5',
10
+ mini: {
11
+ postcss: { pxtransform: { enable: true, config: {} }, url: { enable: true, config: { limit: 1024 } }, cssModules: { enable: false, config: { namingPattern: 'module', generateScopedName: '[name]__[local]___[hash:base64:5]' } } },
12
+ miniCssExtractPluginOption: { ignoreOrder: true },
13
+ webpackChain(chain) { chain.resolve.plugin('tsconfig-paths').use(TsconfigPathsPlugin) }
14
+ }
15
+ }
16
+ return process.env.NODE_ENV === 'development' ? merge({}, base, devConfig) : merge({}, base, prodConfig)
17
+ })
@@ -0,0 +1,2 @@
1
+ import type { UserConfigExport } from '@tarojs/cli'
2
+ export default { mini: {} } satisfies UserConfigExport
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "mini-scaffold-miniprogram", "version": "1.0.0", "private": true, "description": "Taro + Go mini program",
3
+ "scripts": { "dev:weapp": "taro build --type weapp --watch", "build:weapp": "taro build --type weapp", "dev:h5": "taro build --type h5 --watch", "build:h5": "taro build --type h5" },
4
+ "browserslist": ["last 3 versions", "Android >= 4.1", "ios >= 8"],
5
+ "dependencies": {
6
+ "@babel/runtime": "^7.21.5", "@tarojs/components": "3.6.31", "@tarojs/helper": "3.6.31",
7
+ "@tarojs/plugin-framework-react": "3.6.31", "@tarojs/plugin-platform-weapp": "3.6.31",
8
+ "@tarojs/react": "3.6.31", "@tarojs/runtime": "3.6.31", "@tarojs/shared": "3.6.31",
9
+ "@tarojs/taro": "3.6.31", "@tarojs/taro-loader": "3.6.31", "react": "^18.0.0"
10
+ },
11
+ "devDependencies": {
12
+ "@babel/core": "^7.21.8", "@tarojs/cli": "3.6.31", "@tarojs/webpack5-runner": "3.6.31",
13
+ "@types/react": "^18.0.0", "babel-preset-taro": "3.6.31", "tsconfig-paths-webpack-plugin": "^4.0.1",
14
+ "typescript": "^5.1.0", "webpack": "5.88.2"
15
+ }
16
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "miniprogramRoot": "dist/",
3
+ "projectname": "mini-scaffold",
4
+ "description": "Taro + Go 小程序脚手架",
5
+ "appid": "touristappid",
6
+ "setting": {
7
+ "urlCheck": true,
8
+ "es6": false,
9
+ "enhance": false,
10
+ "compileHotReLoad": false,
11
+ "postcss": false,
12
+ "minified": false,
13
+ "newFeature": true,
14
+ "coverView": true,
15
+ "nodeModules": false,
16
+ "autoAudits": false,
17
+ "showShadowRootInWxmlPanel": true,
18
+ "scopeDataCheck": false,
19
+ "uglifyFileName": false,
20
+ "checkInvalidKey": true,
21
+ "checkSiteMap": true,
22
+ "uploadWithSourceMap": true,
23
+ "babelSetting": {
24
+ "ignore": [],
25
+ "disablePlugins": [],
26
+ "outputPath": ""
27
+ }
28
+ },
29
+ "compileType": "miniprogram",
30
+ "condition": {}
31
+ }
@@ -0,0 +1,9 @@
1
+ const fs=require('fs'),path=require('path'),zlib=require('zlib')
2
+ const D=path.join(__dirname,'..','assets','tabbar'); fs.mkdirSync(D,{recursive:true})
3
+ function crc(b){let c=0xFFFFFFFF;for(let i=0;i<b.length;i++){c^=b[i];for(let j=0;j<8;j++)c=(c>>>1)^(c&1?0xEDB88320:0)}return(c^0xFFFFFFFF)>>>0}
4
+ function mk(t,d){const l=Buffer.alloc(4);l.writeUInt32BE(d.length,0);const tp=Buffer.from(t,'ascii');const cr=Buffer.alloc(4);cr.writeUInt32BE(crc(Buffer.concat([tp,d])),0);return Buffer.concat([l,tp,d,cr])}
5
+ function png(w,h,r,g,b){const p=[];for(let y=0;y<h;y++){p.push(0);for(let x=0;x<w;x++){Math.sqrt((x-32)**2+(y-32)**2)<=28?p.push(r,g,b):p.push(255,255,255)}}
6
+ return Buffer.concat([Buffer.from([137,80,78,71,13,10,26,10]),mk('IHDR',Buffer.from([0,0,0,w,0,0,0,h,8,2,0,0,0])),mk('IDAT',zlib.deflateSync(Buffer.from(p))),mk('IEND',Buffer.alloc(0))])}
7
+ const I=[{n:'home',c:'999999',a:'1890ff'},{n:'discover',c:'999999',a:'1890ff'},{n:'mine',c:'999999',a:'1890ff'}]
8
+ for(const{n,c,a}of I){const cr=h=>[parseInt(h.slice(1,3),16),parseInt(h.slice(3,5),16),parseInt(h.slice(5,7),16)];const[r1,g1,b1]=cr(c),[r2,g2,b2]=cr(a)
9
+ fs.writeFileSync(path.join(D,`${n}.png`),png(81,81,r1,g1,b1));fs.writeFileSync(path.join(D,`${n}-active.png`),png(81,81,r2,g2,b2));console.log(`✓ ${n}`)}
@@ -0,0 +1,9 @@
1
+ @import './styles/variables.scss';
2
+
3
+ page {
4
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
5
+ font-size: 14px;
6
+ line-height: 1.5;
7
+ color: $text-color;
8
+ background-color: $bg-color;
9
+ }
@@ -0,0 +1,16 @@
1
+ import { Component, PropsWithChildren } from 'react'
2
+ import './app.scss'
3
+
4
+ class App extends Component<PropsWithChildren> {
5
+ componentDidMount() {}
6
+
7
+ componentDidShow() {}
8
+
9
+ componentDidHide() {}
10
+
11
+ render() {
12
+ return this.props.children
13
+ }
14
+ }
15
+
16
+ export default App
@@ -0,0 +1,2 @@
1
+ .fullscreen-layout { position: relative; width: 100%; min-height: 100vh; display: flex; flex-direction: column; background-color: #fff;
2
+ &--safe { padding-top: env(safe-area-inset-top); } }
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react'
2
+ import { View } from '@tarojs/components'
3
+ import './index.scss'
4
+ export default function FullscreenLayout({ children, className = '', safeArea = true }: { children: ReactNode; className?: string; safeArea?: boolean }) {
5
+ return <View className={`fullscreen-layout ${safeArea ? 'fullscreen-layout--safe' : ''} ${className}`}>{children}</View>
6
+ }
@@ -0,0 +1,11 @@
1
+ @import '../../styles/variables.scss';
2
+ .header-tabbar-layout { width: 100%; min-height: 100vh; display: flex; flex-direction: column; background-color: $bg-color;
3
+ &__header { position: sticky; top: 0; z-index: 100; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 0 0 $border-radius-lg $border-radius-lg; overflow: hidden; }
4
+ &__header-content { display: flex; align-items: center; justify-content: space-between; padding: $spacing-md $spacing-lg; }
5
+ &__title-area { flex: 1; min-width: 0; }
6
+ &__title { display: block; font-size: var(--fs-xxl); font-weight: 800; color: $text-color-inverse; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
7
+ &__subtitle { display: block; margin-top: $spacing-xs; font-size: var(--fs-sm); color: rgba(255,255,255,0.8); }
8
+ &__action { flex-shrink: 0; margin-left: $spacing-md; display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; }
9
+ &__search { padding: 0 $spacing-lg $spacing-md; }
10
+ &__search-input { width: 100%; height: 64rpx; padding: 0 $spacing-lg; background: rgba(255,255,255,0.9); border-radius: 32rpx; font-size: var(--fs-sm); color: $text-color; }
11
+ &__body { flex: 1; min-height: 0; display: flex; flex-direction: column; padding: $spacing-lg; } }
@@ -0,0 +1,20 @@
1
+ import { ReactNode } from 'react'
2
+ import { View, Text, Input } from '@tarojs/components'
3
+ import Taro from '@tarojs/taro'
4
+ import './index.scss'
5
+ export default function HeaderTabBarLayout({ title, subtitle, showSearch = false, onSearch, rightAction, children, className = '' }: { title: string; subtitle?: string; showSearch?: boolean; onSearch?: (k: string) => void; rightAction?: ReactNode; children: ReactNode; className?: string }) {
6
+ const sb = Taro.getSystemInfoSync().statusBarHeight || 20
7
+ return <View className={`header-tabbar-layout ${className}`}>
8
+ <View className='header-tabbar-layout__header' style={{ paddingTop: `${sb}px` }}>
9
+ <View className='header-tabbar-layout__header-content'>
10
+ <View className='header-tabbar-layout__title-area'>
11
+ <Text className='header-tabbar-layout__title'>{title}</Text>
12
+ {subtitle && <Text className='header-tabbar-layout__subtitle'>{subtitle}</Text>}
13
+ </View>
14
+ {rightAction && <View className='header-tabbar-layout__action'>{rightAction}</View>}
15
+ </View>
16
+ {showSearch && <View className='header-tabbar-layout__search'><Input className='header-tabbar-layout__search-input' type='text' placeholder='搜索...' confirmType='search' onConfirm={(e) => onSearch?.(e.detail.value)} /></View>}
17
+ </View>
18
+ <View className='header-tabbar-layout__body'>{children}</View>
19
+ </View>
20
+ }
@@ -0,0 +1,7 @@
1
+ # 布局系统
2
+
3
+ | 布局 | Header | TabBar | 场景 |
4
+ |------|--------|--------|------|
5
+ | `FullscreenLayout` | ❌ | ❌ | 启动页/活动页 |
6
+ | `TabBarLayout` | 原生 | ✅ | 标准列表页 |
7
+ | `HeaderTabBarLayout` | ✅ 自定义 | ✅ | 品牌化首页 |
@@ -0,0 +1 @@
1
+ .tabbar-layout { width: 100%; min-height: 100vh; display: flex; flex-direction: column; background-color: #f5f5f5; }
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from 'react'
2
+ import { View } from '@tarojs/components'
3
+ import './index.scss'
4
+ export default function TabBarLayout({ children, className = '' }: { children: ReactNode; className?: string }) {
5
+ return <View className={`tabbar-layout ${className}`}>{children}</View>
6
+ }
@@ -0,0 +1,3 @@
1
+ export { default as FullscreenLayout } from './FullscreenLayout'
2
+ export { default as TabBarLayout } from './TabBarLayout'
3
+ export { default as HeaderTabBarLayout } from './HeaderTabBarLayout'
@@ -0,0 +1 @@
1
+ export default definePageConfig({ navigationBarTitleText: '发现' })
@@ -0,0 +1,4 @@
1
+ @import '../../styles/variables.scss';
2
+ .discover { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: $spacing-xl;
3
+ &__title { display: block; font-size: var(--fs-xxl); font-weight: 700; color: $text-color; margin-bottom: $spacing-md; }
4
+ &__desc { display: block; font-size: var(--fs-sm); color: $text-color-secondary; } }
@@ -0,0 +1,6 @@
1
+ import { View, Text } from '@tarojs/components'
2
+ import { TabBarLayout } from '@/layouts'
3
+ import './index.scss'
4
+ export default function DiscoverPage() {
5
+ return <TabBarLayout><View className='discover'><Text className='discover__title'>发现</Text><Text className='discover__desc'>标准 TabBar 布局</Text></View></TabBarLayout>
6
+ }
@@ -0,0 +1 @@
1
+ export default definePageConfig({ navigationStyle: 'custom' })
@@ -0,0 +1,6 @@
1
+ @import '../../styles/variables.scss';
2
+ .home { flex: 1;
3
+ &__card { background: $bg-color-white; border-radius: $border-radius-lg; padding: $spacing-xl; box-shadow: $shadow-base; }
4
+ &__card-title { display: block; font-size: var(--fs-lg); font-weight: 700; color: $text-color; margin-bottom: $spacing-md; padding-bottom: $spacing-md; border-bottom: 1px solid $border-color-light; }
5
+ &__card-body { display: block; font-size: var(--fs-base); color: $text-color-secondary; margin-bottom: $spacing-xl; text-align: center; padding: $spacing-xl 0; }
6
+ &__btn { width: 100%; height: 80rpx; line-height: 80rpx; background: $primary-color; color: $text-color-inverse; font-size: var(--fs-base); border-radius: $border-radius-base; border: none; } }
@@ -0,0 +1,17 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { View, Text, Button } from '@tarojs/components'
3
+ import Taro from '@tarojs/taro'
4
+ import { HeaderTabBarLayout } from '@/layouts'
5
+ import './index.scss'
6
+ export default function HomePage() {
7
+ const [g, setG] = useState(''), [loading, setLoading] = useState(false)
8
+ const fetch = async () => { setLoading(true); try { const r = await Taro.request({ url: 'http://localhost:8080/api/hello' }); const d = r.data as any; if (d.code === 0) setG(d.data.greeting) } catch { Taro.showToast({ title: '网络错误', icon: 'none' }) } finally { setLoading(false) } }
9
+ useEffect(() => { fetch() }, [])
10
+ return <HeaderTabBarLayout title='首页' subtitle='Taro + Go 脚手架' showSearch onSearch={(k) => Taro.showToast({ title: `搜索: ${k}`, icon: 'none' })}>
11
+ <View className='home'><View className='home__card'>
12
+ <Text className='home__card-title'>Hello World</Text>
13
+ <Text className='home__card-body'>{loading ? '加载中...' : g || '暂无数据'}</Text>
14
+ <Button className='home__btn' onClick={fetch} loading={loading}>刷新</Button>
15
+ </View></View>
16
+ </HeaderTabBarLayout>
17
+ }
@@ -0,0 +1 @@
1
+ export default definePageConfig({ navigationBarTitleText: '我的' })
@@ -0,0 +1,4 @@
1
+ @import '../../styles/variables.scss';
2
+ .mine { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: $spacing-xl;
3
+ &__title { display: block; font-size: var(--fs-xxl); font-weight: 700; color: $text-color; margin-bottom: $spacing-md; }
4
+ &__desc { display: block; font-size: var(--fs-sm); color: $text-color-secondary; } }
@@ -0,0 +1,6 @@
1
+ import { View, Text } from '@tarojs/components'
2
+ import { TabBarLayout } from '@/layouts'
3
+ import './index.scss'
4
+ export default function MinePage() {
5
+ return <TabBarLayout><View className='mine'><Text className='mine__title'>我的</Text><Text className='mine__desc'>个人中心</Text></View></TabBarLayout>
6
+ }
@@ -0,0 +1 @@
1
+ export default definePageConfig({ navigationStyle: 'custom', backgroundColor: '#000000' })
@@ -0,0 +1,11 @@
1
+ @import '../../styles/variables.scss';
2
+ .splash { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; flex-direction: column; align-items: center; justify-content: space-between;
3
+ &__content { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: $spacing-xl; }
4
+ &__logo { width: 160rpx; height: 160rpx; border-radius: 50%; background: rgba(255,255,255,0.2); display: flex; align-items: center; justify-content: center; margin-bottom: $spacing-xl; }
5
+ &__logo-text { font-size: 56rpx; font-weight: 800; color: #fff; }
6
+ &__title { display: block; font-size: var(--fs-hero); font-weight: 800; color: #fff; margin-bottom: $spacing-md; }
7
+ &__subtitle { display: block; font-size: var(--fs-base); color: rgba(255,255,255,0.8); margin-bottom: 48px; }
8
+ &__btn { width: 400rpx; height: 96rpx; border-radius: 48rpx; background: #fff; display: flex; align-items: center; justify-content: center; }
9
+ &__btn-text { font-size: var(--fs-lg); font-weight: 600; color: #667eea; }
10
+ &__footer { padding-bottom: 80rpx; }
11
+ &__footer-text { font-size: var(--fs-xs); color: rgba(255,255,255,0.5); } }
@@ -0,0 +1,15 @@
1
+ import { View, Text } from '@tarojs/components'
2
+ import Taro from '@tarojs/taro'
3
+ import { FullscreenLayout } from '@/layouts'
4
+ import './index.scss'
5
+ export default function SplashPage() {
6
+ return <FullscreenLayout safeArea={false} className='splash'>
7
+ <View className='splash__content'>
8
+ <View className='splash__logo'><Text className='splash__logo-text'>MS</Text></View>
9
+ <Text className='splash__title'>Mini Scaffold</Text>
10
+ <Text className='splash__subtitle'>Taro + Go 小程序脚手架</Text>
11
+ <View className='splash__btn' onClick={() => Taro.switchTab({ url: '/pages/home/index' })}><Text className='splash__btn-text'>进入</Text></View>
12
+ </View>
13
+ <View className='splash__footer'><Text className='splash__footer-text'>全屏沉浸式布局</Text></View>
14
+ </FullscreenLayout>
15
+ }
@@ -0,0 +1,41 @@
1
+ // Colors
2
+ $primary-color: #1890ff;
3
+ $success-color: #52c41a;
4
+ $warning-color: #faad14;
5
+ $error-color: #f5222d;
6
+
7
+ // Text colors
8
+ $text-color: #333333;
9
+ $text-color-secondary: #666666;
10
+ $text-color-inverse: #ffffff;
11
+
12
+ // Background colors
13
+ $bg-color: #f5f5f5;
14
+ $bg-color-white: #ffffff;
15
+
16
+ // Border colors
17
+ $border-color: #d9d9d9;
18
+ $border-color-light: #e8e8e8;
19
+
20
+ // Font sizes
21
+ $font-size-sm: 12px;
22
+ $font-size-base: 14px;
23
+ $font-size-lg: 16px;
24
+ $font-size-xl: 18px;
25
+
26
+ // Spacing
27
+ $spacing-xs: 4px;
28
+ $spacing-sm: 8px;
29
+ $spacing-md: 12px;
30
+ $spacing-lg: 16px;
31
+ $spacing-xl: 24px;
32
+
33
+ // Border radius
34
+ $border-radius-sm: 2px;
35
+ $border-radius-base: 4px;
36
+ $border-radius-lg: 8px;
37
+
38
+ // Shadows
39
+ $shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px 0 rgba(0, 0, 0, 0.02);
40
+ $shadow-base: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
41
+ $shadow-lg: 0 6px 16px -8px rgba(0, 0, 0, 0.08), 0 9px 28px 0 rgba(0, 0, 0, 0.05), 0 12px 48px 16px rgba(0, 0, 0, 0.03);
@@ -0,0 +1,31 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2017",
4
+ "module": "commonjs",
5
+ "removeComments": false,
6
+ "preserveConstEnums": true,
7
+ "moduleResolution": "node",
8
+ "experimentalDecorators": true,
9
+ "noImplicitAny": false,
10
+ "allowSyntheticDefaultImports": true,
11
+ "outDir": "lib",
12
+ "noUnusedLocals": true,
13
+ "noUnusedParameters": true,
14
+ "strictNullChecks": true,
15
+ "sourceMap": true,
16
+ "rootDir": ".",
17
+ "jsx": "react-jsx",
18
+ "allowJs": true,
19
+ "resolveJsonModule": true,
20
+ "typeRoots": [
21
+ "node_modules/@types",
22
+ "global.d.ts"
23
+ ],
24
+ "paths": {
25
+ "@/*": ["./src/*"]
26
+ },
27
+ "baseUrl": "."
28
+ },
29
+ "include": ["src", "types", "config"],
30
+ "compileOnSave": false
31
+ }
@@ -0,0 +1,18 @@
1
+ /// <reference types="@tarojs/taro" />
2
+
3
+ declare module '*.png';
4
+ declare module '*.gif';
5
+ declare module '*.jpg';
6
+ declare module '*.jpeg';
7
+ declare module '*.svg';
8
+ declare module '*.css';
9
+ declare module '*.less';
10
+ declare module '*.scss';
11
+ declare module '*.sass';
12
+ declare module '*.styl';
13
+
14
+ declare namespace NodeJS {
15
+ interface ProcessEnv {
16
+ TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd' | 'harmony-hybrid'
17
+ }
18
+ }
@@ -0,0 +1,13 @@
1
+ # Server configuration
2
+ PORT=8080
3
+
4
+ # Application configuration
5
+ APP_NAME=Mini Scaffold API
6
+ APP_ENV=development
7
+
8
+ # Database configuration (example)
9
+ # DB_HOST=localhost
10
+ # DB_PORT=5432
11
+ # DB_USER=postgres
12
+ # DB_PASSWORD=password
13
+ # DB_NAME=mini_scaffold
@@ -0,0 +1,76 @@
1
+ .PHONY: build run clean test dev
2
+
3
+ # Variables
4
+ APP_NAME := server
5
+ BUILD_DIR := ./bin
6
+ GO := go
7
+
8
+ # Build the application
9
+ build:
10
+ @echo "Building $(APP_NAME)..."
11
+ @mkdir -p $(BUILD_DIR)
12
+ $(GO) build -o $(BUILD_DIR)/$(APP_NAME) main.go
13
+ @echo "Build complete: $(BUILD_DIR)/$(APP_NAME)"
14
+
15
+ # Run the application
16
+ run: build
17
+ @echo "Starting $(APP_NAME)..."
18
+ $(BUILD_DIR)/$(APP_NAME)
19
+
20
+ # Run in development mode with hot reload (requires air)
21
+ dev:
22
+ @echo "Starting development server..."
23
+ @which air > /dev/null 2>&1 || (echo "Installing air..." && go install github.com/cosmtrek/air@latest)
24
+ air
25
+
26
+ # Clean build artifacts
27
+ clean:
28
+ @echo "Cleaning build artifacts..."
29
+ @rm -rf $(BUILD_DIR)
30
+ @echo "Clean complete"
31
+
32
+ # Run tests
33
+ test:
34
+ @echo "Running tests..."
35
+ $(GO) test -v ./...
36
+
37
+ # Format code
38
+ fmt:
39
+ @echo "Formatting code..."
40
+ $(GO) fmt ./...
41
+
42
+ # Vet code
43
+ vet:
44
+ @echo "Vetting code..."
45
+ $(GO) vet ./...
46
+
47
+ # Lint code (requires golangci-lint)
48
+ lint:
49
+ @echo "Linting code..."
50
+ @which golangci-lint > /dev/null 2>&1 || (echo "Installing golangci-lint..." && go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest)
51
+ golangci-lint run
52
+
53
+ # Download dependencies
54
+ deps:
55
+ @echo "Downloading dependencies..."
56
+ $(GO) mod download
57
+
58
+ # Tidy dependencies
59
+ tidy:
60
+ @echo "Tidying dependencies..."
61
+ $(GO) mod tidy
62
+
63
+ # Show help
64
+ help:
65
+ @echo "Available targets:"
66
+ @echo " build - Build the application"
67
+ @echo " run - Build and run the application"
68
+ @echo " dev - Run in development mode with hot reload"
69
+ @echo " clean - Clean build artifacts"
70
+ @echo " test - Run tests"
71
+ @echo " fmt - Format code"
72
+ @echo " vet - Vet code"
73
+ @echo " lint - Lint code"
74
+ @echo " deps - Download dependencies"
75
+ @echo " tidy - Tidy dependencies"
76
+ @echo " help - Show this help"
@@ -0,0 +1,134 @@
1
+ # Mini Scaffold Server
2
+
3
+ A simple Go backend server built with Fiber framework.
4
+
5
+ ## Prerequisites
6
+
7
+ - Go 1.21 or higher
8
+
9
+ ## Getting Started
10
+
11
+ ### Install dependencies
12
+
13
+ ```bash
14
+ go mod tidy
15
+ ```
16
+
17
+ ### Run the server
18
+
19
+ ```bash
20
+ # Using make
21
+ make run
22
+
23
+ # Or directly
24
+ go run main.go
25
+ ```
26
+
27
+ ### Development mode
28
+
29
+ ```bash
30
+ # Using make (requires air for hot reload)
31
+ make dev
32
+ ```
33
+
34
+ ## API Endpoints
35
+
36
+ ### Health Check
37
+
38
+ ```
39
+ GET /api/health
40
+ ```
41
+
42
+ Response:
43
+ ```json
44
+ {
45
+ "code": 200,
46
+ "message": "OK",
47
+ "data": {
48
+ "status": "healthy"
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### Hello World
54
+
55
+ ```
56
+ GET /api/hello
57
+ ```
58
+
59
+ Response:
60
+ ```json
61
+ {
62
+ "code": 200,
63
+ "message": "success",
64
+ "data": {
65
+ "greeting": "Hello, World! 来自 Go 后端",
66
+ "timestamp": "2024-01-01T00:00:00Z",
67
+ "version": "1.0.0"
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### Hello with Name
73
+
74
+ ```
75
+ POST /api/hello
76
+ Content-Type: application/json
77
+
78
+ {
79
+ "name": "YourName"
80
+ }
81
+ ```
82
+
83
+ Response:
84
+ ```json
85
+ {
86
+ "code": 200,
87
+ "message": "success",
88
+ "data": {
89
+ "greeting": "Hello, YourName! 来自 Go 后端",
90
+ "timestamp": "2024-01-01T00:00:00Z"
91
+ }
92
+ }
93
+ ```
94
+
95
+ ## Configuration
96
+
97
+ The server can be configured using environment variables or a `.env` file:
98
+
99
+ - `PORT` - Server port (default: 8080)
100
+ - `APP_NAME` - Application name
101
+ - `APP_ENV` - Application environment (development/production)
102
+
103
+ ## Project Structure
104
+
105
+ ```
106
+ server/
107
+ ├── main.go # Entry point
108
+ ├── go.mod # Go module file
109
+ ├── go.sum # Go dependencies checksum
110
+ ├── .env # Environment variables
111
+ ├── .env.example # Example environment variables
112
+ ├── Makefile # Build and run commands
113
+ └── README.md # This file
114
+ ```
115
+
116
+ ## Building for Production
117
+
118
+ ```bash
119
+ # Build binary
120
+ make build
121
+
122
+ # Run binary
123
+ ./bin/server
124
+ ```
125
+
126
+ ## Testing
127
+
128
+ ```bash
129
+ make test
130
+ ```
131
+
132
+ ## License
133
+
134
+ MIT
@@ -0,0 +1,25 @@
1
+ module mini-scaffold-server
2
+
3
+ go 1.21
4
+
5
+ require (
6
+ github.com/gofiber/fiber/v2 v2.52.0
7
+ github.com/joho/godotenv v1.5.1
8
+ )
9
+
10
+ require (
11
+ github.com/andybalholm/brotli v1.0.5 // indirect
12
+ github.com/google/uuid v1.5.0 // indirect
13
+ github.com/klauspost/compress v1.17.4 // indirect
14
+ github.com/mattn/go-colorable v0.1.13 // indirect
15
+ github.com/mattn/go-isatty v0.0.20 // indirect
16
+ github.com/mattn/go-runewidth v0.0.15 // indirect
17
+ github.com/philhofer/fwd v1.1.2 // indirect
18
+ github.com/rivo/uniseg v0.4.4 // indirect
19
+ github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
20
+ github.com/tinylib/msgp v1.1.9 // indirect
21
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
22
+ github.com/valyala/fasthttp v1.51.0 // indirect
23
+ github.com/valyala/tcplisten v1.0.0 // indirect
24
+ golang.org/x/sys v0.15.0 // indirect
25
+ )
@@ -0,0 +1,126 @@
1
+ package main
2
+
3
+ import (
4
+ "log"
5
+ "os"
6
+
7
+ "github.com/gofiber/fiber/v2"
8
+ "github.com/gofiber/fiber/v2/middleware/cors"
9
+ "github.com/gofiber/fiber/v2/middleware/logger"
10
+ "github.com/gofiber/fiber/v2/middleware/recover"
11
+ "github.com/joho/godotenv"
12
+ )
13
+
14
+ func main() {
15
+ // Load .env file if it exists
16
+ if err := godotenv.Load(); err != nil {
17
+ log.Println("No .env file found, using default configuration")
18
+ }
19
+
20
+ // Get port from environment variable or use default
21
+ port := os.Getenv("PORT")
22
+ if port == "" {
23
+ port = "8080"
24
+ }
25
+
26
+ // Create Fiber app
27
+ app := fiber.New(fiber.Config{
28
+ AppName: "Mini Scaffold API",
29
+ ServerHeader: "Mini Scaffold",
30
+ ErrorHandler: func(c *fiber.Ctx, err error) error {
31
+ code := fiber.StatusInternalServerError
32
+ if e, ok := err.(*fiber.Error); ok {
33
+ code = e.Code
34
+ }
35
+ return c.Status(code).JSON(fiber.Map{
36
+ "code": code,
37
+ "message": err.Error(),
38
+ })
39
+ },
40
+ })
41
+
42
+ // Middleware
43
+ app.Use(recover.New())
44
+ app.Use(logger.New(logger.Config{
45
+ Format: "${time} ${method} ${path} ${status} ${latency}\n",
46
+ }))
47
+ app.Use(cors.New(cors.Config{
48
+ AllowOrigins: "*",
49
+ AllowMethods: "GET,POST,PUT,DELETE,OPTIONS",
50
+ AllowHeaders: "Origin,Content-Type,Accept,Authorization",
51
+ }))
52
+
53
+ // Routes
54
+ setupRoutes(app)
55
+
56
+ // Start server
57
+ log.Printf("Server starting on port %s", port)
58
+ if err := app.Listen(":" + port); err != nil {
59
+ log.Fatalf("Failed to start server: %v", err)
60
+ }
61
+ }
62
+
63
+ func setupRoutes(app *fiber.App) {
64
+ // API group
65
+ api := app.Group("/api")
66
+
67
+ // Health check
68
+ api.Get("/health", func(c *fiber.Ctx) error {
69
+ return c.JSON(fiber.Map{
70
+ "code": 200,
71
+ "message": "OK",
72
+ "data": fiber.Map{
73
+ "status": "healthy",
74
+ },
75
+ })
76
+ })
77
+
78
+ // Hello world endpoint
79
+ api.Get("/hello", func(c *fiber.Ctx) error {
80
+ return c.JSON(fiber.Map{
81
+ "code": 200,
82
+ "message": "success",
83
+ "data": fiber.Map{
84
+ "greeting": "Hello, World! 来自 Go 后端",
85
+ "timestamp": "2024-01-01T00:00:00Z",
86
+ "version": "1.0.0",
87
+ },
88
+ })
89
+ })
90
+
91
+ // Example POST endpoint
92
+ api.Post("/hello", func(c *fiber.Ctx) error {
93
+ type Request struct {
94
+ Name string `json:"name"`
95
+ }
96
+
97
+ var req Request
98
+ if err := c.BodyParser(&req); err != nil {
99
+ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
100
+ "code": 400,
101
+ "message": "Invalid request body",
102
+ })
103
+ }
104
+
105
+ if req.Name == "" {
106
+ req.Name = "World"
107
+ }
108
+
109
+ return c.JSON(fiber.Map{
110
+ "code": 200,
111
+ "message": "success",
112
+ "data": fiber.Map{
113
+ "greeting": "Hello, " + req.Name + "! 来自 Go 后端",
114
+ "timestamp": "2024-01-01T00:00:00Z",
115
+ },
116
+ })
117
+ })
118
+
119
+ // Catch-all route for 404
120
+ app.Use(func(c *fiber.Ctx) error {
121
+ return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
122
+ "code": 404,
123
+ "message": "Route not found",
124
+ })
125
+ })
126
+ }