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