init
init
This commit is contained in:
46
CHANGELOG.md
Normal file
46
CHANGELOG.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 更新日誌
|
||||
|
||||
本專案的所有重要變更都會記錄在此檔案中。
|
||||
|
||||
格式基於 [Keep a Changelog](https://keepachangelog.com/zh-TW/1.0.0/),
|
||||
且本專案遵循 [Semantic Versioning](https://semver.org/lang/zh-TW/)。
|
||||
|
||||
## [未發布]
|
||||
|
||||
### 新增
|
||||
- 新功能 A
|
||||
- 新功能 B
|
||||
|
||||
### 變更
|
||||
- 變更功能 A
|
||||
- 變更功能 B
|
||||
|
||||
### 修復
|
||||
- 修復 Bug A
|
||||
- 修復 Bug B
|
||||
|
||||
## [1.0.0] - 2024-01-01
|
||||
|
||||
### 新增
|
||||
- 初始版本發布
|
||||
- 基本圖片上傳功能
|
||||
- React 前端介面
|
||||
- Flask 後端 API
|
||||
- 圖片預覽功能
|
||||
- 響應式設計
|
||||
- Tailwind CSS 樣式
|
||||
|
||||
### 技術特色
|
||||
- Flask 3.0.0 後端框架
|
||||
- React 19 前端框架
|
||||
- Flask-CORS 跨域支援
|
||||
- 現代化 UI/UX 設計
|
||||
- 支援多種圖片格式
|
||||
|
||||
---
|
||||
|
||||
## 版本說明
|
||||
|
||||
- **主要版本**:不相容的 API 變更
|
||||
- **次要版本**:向後相容的新功能
|
||||
- **修補版本**:向後相容的 Bug 修復
|
168
CONTRIBUTING.md
Normal file
168
CONTRIBUTING.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# 貢獻指南
|
||||
|
||||
感謝您對 ImageZoom 專案的關注!我們歡迎所有形式的貢獻。
|
||||
|
||||
## 如何貢獻
|
||||
|
||||
### 報告 Bug
|
||||
|
||||
如果您發現了 Bug,請:
|
||||
|
||||
1. 檢查 [Issues](https://github.com/your-username/imagezoom/issues) 是否已經有人報告過
|
||||
2. 如果沒有,請建立新的 Issue,並包含:
|
||||
- 詳細的 Bug 描述
|
||||
- 重現步驟
|
||||
- 預期行為和實際行為
|
||||
- 作業系統和瀏覽器版本
|
||||
- 錯誤訊息或截圖
|
||||
|
||||
### 功能請求
|
||||
|
||||
如果您有新的功能想法:
|
||||
|
||||
1. 檢查現有的 Issues 和 Pull Requests
|
||||
2. 建立新的 Issue,描述:
|
||||
- 功能需求
|
||||
- 使用場景
|
||||
- 預期效果
|
||||
|
||||
### 程式碼貢獻
|
||||
|
||||
#### 開發環境設定
|
||||
|
||||
1. **Fork 專案**
|
||||
```bash
|
||||
git clone https://github.com/your-username/imagezoom.git
|
||||
cd imagezoom
|
||||
```
|
||||
|
||||
2. **建立虛擬環境**
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # Linux/Mac
|
||||
# 或
|
||||
venv\Scripts\activate # Windows
|
||||
```
|
||||
|
||||
3. **安裝依賴**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
cd frontend && npm install
|
||||
```
|
||||
|
||||
#### 開發流程
|
||||
|
||||
1. **建立功能分支**
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
|
||||
2. **開發功能**
|
||||
- 遵循現有的程式碼風格
|
||||
- 撰寫測試(如果適用)
|
||||
- 確保所有測試通過
|
||||
|
||||
3. **提交變更**
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat: 新增功能描述"
|
||||
```
|
||||
|
||||
4. **推送到分支**
|
||||
```bash
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
|
||||
5. **建立 Pull Request**
|
||||
- 前往 GitHub 建立 Pull Request
|
||||
- 填寫 PR 模板
|
||||
- 等待審查
|
||||
|
||||
#### 提交訊息規範
|
||||
|
||||
我們使用 [Conventional Commits](https://www.conventionalcommits.org/) 規範:
|
||||
|
||||
- `feat:` 新功能
|
||||
- `fix:` Bug 修復
|
||||
- `docs:` 文件更新
|
||||
- `style:` 程式碼格式調整
|
||||
- `refactor:` 重構
|
||||
- `test:` 測試相關
|
||||
- `chore:` 建置工具或輔助工具的變動
|
||||
|
||||
範例:
|
||||
```
|
||||
feat: 新增圖片縮放功能
|
||||
fix: 修復上傳檔案大小限制問題
|
||||
docs: 更新 API 文件
|
||||
```
|
||||
|
||||
#### 程式碼風格
|
||||
|
||||
**Python (後端)**
|
||||
- 遵循 PEP 8 規範
|
||||
- 使用 4 個空格縮排
|
||||
- 行長度限制在 79 字元內
|
||||
- 使用有意義的變數和函數名稱
|
||||
|
||||
**JavaScript (前端)**
|
||||
- 使用 ESLint 和 Prettier
|
||||
- 遵循 React 最佳實踐
|
||||
- 使用有意義的組件和函數名稱
|
||||
|
||||
#### 測試
|
||||
|
||||
**後端測試**
|
||||
```bash
|
||||
# 執行測試
|
||||
python -m pytest
|
||||
|
||||
# 執行測試並顯示覆蓋率
|
||||
python -m pytest --cov=app
|
||||
```
|
||||
|
||||
**前端測試**
|
||||
```bash
|
||||
cd frontend
|
||||
npm test
|
||||
```
|
||||
|
||||
## Pull Request 審查流程
|
||||
|
||||
1. **自動檢查**
|
||||
- CI/CD 流程會自動執行測試
|
||||
- 程式碼風格檢查
|
||||
- 安全性掃描
|
||||
|
||||
2. **人工審查**
|
||||
- 至少需要一位維護者審查
|
||||
- 可能需要修改或改進
|
||||
|
||||
3. **合併**
|
||||
- 審查通過後會合併到主分支
|
||||
- 會自動部署到測試環境
|
||||
|
||||
## 行為準則
|
||||
|
||||
我們致力於建立一個友善和包容的社群環境:
|
||||
|
||||
- 尊重所有貢獻者
|
||||
- 使用友善和建設性的語言
|
||||
- 接受建設性的批評
|
||||
- 專注於問題本身,而非個人
|
||||
|
||||
## 聯絡方式
|
||||
|
||||
如果您有任何問題或建議:
|
||||
|
||||
- 建立 [Issue](https://github.com/your-username/imagezoom/issues)
|
||||
- 發送 Email 至 [your-email@example.com]
|
||||
- 加入我們的 [Discord 社群](https://discord.gg/your-server)
|
||||
|
||||
## 致謝
|
||||
|
||||
感謝所有為這個專案做出貢獻的開發者!
|
||||
|
||||
---
|
||||
|
||||
**注意:** 請確保您同意本專案的 [LICENSE](LICENSE) 條款。
|
63
accessible-emoji.js
Normal file
63
accessible-emoji.js
Normal file
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
var _emojiRegex = _interopRequireDefault(require("emoji-regex"));
|
||||
var _jsxAstUtils = require("jsx-ast-utils");
|
||||
var _safeRegexTest = _interopRequireDefault(require("safe-regex-test"));
|
||||
var _schemas = require("../util/schemas");
|
||||
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
||||
var _isHiddenFromScreenReader = _interopRequireDefault(require("../util/isHiddenFromScreenReader"));
|
||||
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
||||
/**
|
||||
* @fileoverview Enforce emojis are wrapped in <span> and provide screen reader access.
|
||||
* @author Ethan Cohen
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
var errorMessage = 'Emojis should be wrapped in <span>, have role="img", and have an accessible description with aria-label or aria-labelledby.';
|
||||
var schema = (0, _schemas.generateObjSchema)();
|
||||
var _default = exports["default"] = {
|
||||
meta: {
|
||||
docs: {
|
||||
description: 'Enforce emojis are wrapped in `<span>` and provide screen reader access.',
|
||||
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/accessible-emoji.md'
|
||||
},
|
||||
deprecated: true,
|
||||
schema: [schema]
|
||||
},
|
||||
create: function create(context) {
|
||||
var elementType = (0, _getElementType["default"])(context);
|
||||
var testEmoji = (0, _safeRegexTest["default"])((0, _emojiRegex["default"])());
|
||||
return {
|
||||
JSXOpeningElement: function JSXOpeningElement(node) {
|
||||
var literalChildValue = node.parent.children.find(function (child) {
|
||||
return child.type === 'Literal' || child.type === 'JSXText';
|
||||
});
|
||||
if (literalChildValue && testEmoji(literalChildValue.value)) {
|
||||
var elementIsHidden = (0, _isHiddenFromScreenReader["default"])(elementType(node), node.attributes);
|
||||
if (elementIsHidden) {
|
||||
return; // emoji is decorative
|
||||
}
|
||||
var rolePropValue = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'role'));
|
||||
var ariaLabelProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-label');
|
||||
var arialLabelledByProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby');
|
||||
var hasLabel = ariaLabelProp !== undefined || arialLabelledByProp !== undefined;
|
||||
var isSpan = elementType(node) === 'span';
|
||||
if (hasLabel === false || rolePropValue !== 'img' || isSpan === false) {
|
||||
context.report({
|
||||
node,
|
||||
message: errorMessage
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
module.exports = exports.default;
|
36
app.py
Normal file
36
app.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from flask import Flask, request, jsonify, send_from_directory
|
||||
from flask_cors import CORS
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
UPLOAD_FOLDER = 'uploads'
|
||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
|
||||
if not os.path.exists(UPLOAD_FOLDER):
|
||||
os.makedirs(UPLOAD_FOLDER)
|
||||
|
||||
@app.route('/upload', methods=['POST'])
|
||||
def upload_file():
|
||||
if 'file' not in request.files:
|
||||
return jsonify({'error': 'No file part'}), 400
|
||||
file = request.files['file']
|
||||
if file.filename == '':
|
||||
return jsonify({'error': 'No selected file'}), 400
|
||||
if file:
|
||||
filename = file.filename
|
||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
||||
return jsonify({'success': True, 'filename': filename})
|
||||
|
||||
@app.route('/images', methods=['GET'])
|
||||
def get_images():
|
||||
images = os.listdir(app.config['UPLOAD_FOLDER'])
|
||||
return jsonify(images)
|
||||
|
||||
@app.route('/uploads/<filename>')
|
||||
def uploaded_file(filename):
|
||||
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
29
docker-compose.yml
Normal file
29
docker-compose.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
imagezoom:
|
||||
build: .
|
||||
ports:
|
||||
- "5000:5000"
|
||||
volumes:
|
||||
- ./uploads:/app/uploads
|
||||
- ./templates:/app/templates
|
||||
environment:
|
||||
- FLASK_ENV=development
|
||||
- FLASK_DEBUG=1
|
||||
restart: unless-stopped
|
||||
|
||||
# 可選:如果需要資料庫
|
||||
# db:
|
||||
# image: postgres:15
|
||||
# environment:
|
||||
# POSTGRES_DB: imagezoom
|
||||
# POSTGRES_USER: postgres
|
||||
# POSTGRES_PASSWORD: password
|
||||
# volumes:
|
||||
# - postgres_data:/var/lib/postgresql/data
|
||||
# ports:
|
||||
# - "5432:5432"
|
||||
|
||||
# volumes:
|
||||
# postgres_data:
|
Reference in New Issue
Block a user