Compare commits

...

13 Commits

8
.idea/.gitignore vendored

@ -1,8 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
</project>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/zyd2025.iml" filepath="$PROJECT_DIR$/.idea/zyd2025.iml" />
</modules>
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$/src" />
<option name="settingsModule" value="settings.py" />
<option name="manageScript" value="$MODULE_DIR$/src/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/src/templates" />
</list>
</option>
</component>
</module>

Binary file not shown.

@ -1,10 +0,0 @@
[run]
source = .
include = *.py
omit =
*migrations*
*tests*
*.html
*whoosh_cn_backend*
*settings.py*
*venv*

@ -8,4 +8,5 @@ settings_production.py
*.md
docs/
logs/
static/
static/
.github/

@ -0,0 +1 @@
DJANGO_SECRET_KEY='f)jnngb81u_)*5!e%zclyr=zoh^61268qhm!kebtl8_c-pp_d^'

@ -38,10 +38,12 @@ jobs:
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: python
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

@ -0,0 +1,176 @@
name: 自动部署到生产环境
on:
workflow_run:
workflows: ["Django CI"]
types:
- completed
branches:
- master
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'production'
type: choice
options:
- production
- staging
image_tag:
description: '镜像标签 (默认: latest)'
required: false
default: 'latest'
type: string
skip_tests:
description: '跳过测试直接部署'
required: false
default: false
type: boolean
env:
REGISTRY: registry.cn-shenzhen.aliyuncs.com
IMAGE_NAME: liangliangyy/djangoblog
NAMESPACE: djangoblog
jobs:
deploy:
name: 构建镜像并部署到生产环境
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
steps:
- name: 检出代码
uses: actions/checkout@v4
- name: 设置部署参数
id: deploy-params
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "trigger_type=手动触发" >> $GITHUB_OUTPUT
echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
echo "image_tag=${{ github.event.inputs.image_tag }}" >> $GITHUB_OUTPUT
echo "skip_tests=${{ github.event.inputs.skip_tests }}" >> $GITHUB_OUTPUT
else
echo "trigger_type=CI自动触发" >> $GITHUB_OUTPUT
echo "environment=production" >> $GITHUB_OUTPUT
echo "image_tag=latest" >> $GITHUB_OUTPUT
echo "skip_tests=false" >> $GITHUB_OUTPUT
fi
- name: 显示部署信息
run: |
echo "🚀 部署信息:"
echo " 触发方式: ${{ steps.deploy-params.outputs.trigger_type }}"
echo " 部署环境: ${{ steps.deploy-params.outputs.environment }}"
echo " 镜像标签: ${{ steps.deploy-params.outputs.image_tag }}"
echo " 跳过测试: ${{ steps.deploy-params.outputs.skip_tests }}"
- name: 设置Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 登录私有镜像仓库
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: 提取镜像元数据
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=sha,prefix={{branch}}-
type=raw,value=${{ steps.deploy-params.outputs.image_tag }}
- name: 构建并推送Docker镜像
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64
- name: 部署到生产服务器
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.PRODUCTION_HOST }}
username: ${{ secrets.PRODUCTION_USER }}
key: ${{ secrets.PRODUCTION_SSH_KEY }}
port: ${{ secrets.PRODUCTION_PORT || 22 }}
script: |
echo "🚀 开始部署 DjangoBlog..."
# 检查kubectl是否可用
if ! command -v kubectl &> /dev/null; then
echo "❌ 错误: kubectl 未安装或不在PATH中"
exit 1
fi
# 检查命名空间是否存在
if ! kubectl get namespace ${{ env.NAMESPACE }} &> /dev/null; then
echo "❌ 错误: 命名空间 ${{ env.NAMESPACE }} 不存在"
exit 1
fi
# 更新deployment镜像
echo "📦 更新deployment镜像为: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.deploy-params.outputs.image_tag }}"
kubectl set image deployment/djangoblog \
djangoblog=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.deploy-params.outputs.image_tag }} \
-n ${{ env.NAMESPACE }}
# 重启deployment
echo "🔄 重启deployment..."
kubectl -n ${{ env.NAMESPACE }} rollout restart deployment djangoblog
# 等待deployment完成
echo "⏳ 等待deployment完成..."
kubectl rollout status deployment/djangoblog -n ${{ env.NAMESPACE }} --timeout=300s
# 检查deployment状态
echo "✅ 检查deployment状态..."
kubectl get deployment djangoblog -n ${{ env.NAMESPACE }}
kubectl get pods -l app=djangoblog -n ${{ env.NAMESPACE }}
echo "🎉 部署完成!"
- name: 发送部署通知
if: always()
run: |
# 设置通知内容
if [ "${{ job.status }}" = "success" ]; then
TITLE="✅ DjangoBlog部署成功"
STATUS="成功"
else
TITLE="❌ DjangoBlog部署失败"
STATUS="失败"
fi
MESSAGE="部署状态: ${STATUS}
触发方式: ${{ steps.deploy-params.outputs.trigger_type }}
部署环境: ${{ steps.deploy-params.outputs.environment }}
镜像标签: ${{ steps.deploy-params.outputs.image_tag }}
提交者: ${{ github.actor }}
时间: $(date '+%Y-%m-%d %H:%M:%S')
查看详情: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
# 发送Server酱通知
if [ -n "${{ secrets.SERVERCHAN_KEY }}" ]; then
echo "{\"title\": \"${TITLE}\", \"desp\": \"${MESSAGE}\"}" > /tmp/serverchan.json
curl --location "https://sctapi.ftqq.com/${{ secrets.SERVERCHAN_KEY }}.send" \
--header "Content-Type: application/json" \
--data @/tmp/serverchan.json \
--silent > /dev/null
rm -f /tmp/serverchan.json
echo "📱 部署通知已发送"
fi

@ -19,55 +19,59 @@ on:
- '**/*.js'
jobs:
build-normal:
test:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
fail-fast: false
matrix:
python-version: ["3.10","3.11" ]
include:
# 标准测试 - Python 3.10
- python-version: "3.10"
test-type: "standard"
database: "mysql"
elasticsearch: false
coverage: false
# 标准测试 - Python 3.11
- python-version: "3.11"
test-type: "standard"
database: "mysql"
elasticsearch: false
coverage: false
# 完整测试 - 包含ES和覆盖率
- python-version: "3.11"
test-type: "full"
database: "mysql"
elasticsearch: true
coverage: true
# Docker构建测试
- python-version: "3.11"
test-type: "docker"
database: "none"
elasticsearch: false
coverage: false
name: Test (${{ matrix.test-type }}, Python ${{ matrix.python-version }})
steps:
- name: Start MySQL
uses: samin/mysql-action@v1.3
with:
host port: 3306
container port: 3306
character set server: utf8mb4
collation server: utf8mb4_general_ci
mysql version: latest
mysql root password: root
mysql database: djangoblog
mysql user: root
mysql password: root
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run Tests
env:
DJANGO_MYSQL_PASSWORD: root
DJANGO_MYSQL_HOST: 127.0.0.1
- name: Checkout代码
uses: actions/checkout@v4
- name: 设置测试信息
id: test-info
run: |
python manage.py makemigrations
python manage.py migrate
python manage.py test
build-with-es:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: ["3.10","3.11" ]
steps:
- name: Start MySQL
echo "test_name=${{ matrix.test-type }}-py${{ matrix.python-version }}" >> $GITHUB_OUTPUT
if [ "${{ matrix.test-type }}" = "docker" ]; then
echo "skip_python_setup=true" >> $GITHUB_OUTPUT
else
echo "skip_python_setup=false" >> $GITHUB_OUTPUT
fi
# MySQL数据库设置 (只有需要数据库的测试才执行)
- name: 启动MySQL数据库
if: matrix.database == 'mysql'
uses: samin/mysql-action@v1.3
with:
host port: 3306
@ -79,58 +83,289 @@ jobs:
mysql database: djangoblog
mysql user: root
mysql password: root
- name: Configure sysctl limits
# Elasticsearch设置 (只有完整测试才执行)
- name: 配置系统参数 (ES)
if: matrix.elasticsearch == true
run: |
sudo swapoff -a
sudo sysctl -w vm.swappiness=1
sudo sysctl -w fs.file-max=262144
sudo sysctl -w vm.max_map_count=262144
- uses: miyataka/elasticsearch-github-actions@1
- name: 启动Elasticsearch
if: matrix.elasticsearch == true
uses: miyataka/elasticsearch-github-actions@1
with:
stack-version: '7.12.1'
plugins: 'https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip'
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
# Python环境设置 (Docker测试跳过)
- name: 设置Python ${{ matrix.python-version }}
if: steps.test-info.outputs.skip_python_setup == 'false'
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install Dependencies
cache-dependency-path: 'requirements.txt'
# 多层缓存策略优化
- name: 缓存Python依赖
if: steps.test-info.outputs.skip_python_setup == 'false'
uses: actions/cache@v4
with:
path: |
~/.cache/pip
.pytest_cache
key: ${{ runner.os }}-python-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('**/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-python-${{ matrix.python-version }}-${{ hashFiles('requirements.txt') }}-
${{ runner.os }}-python-${{ matrix.python-version }}-
${{ runner.os }}-python-
# Django缓存优化 (测试数据库等)
- name: 缓存Django资源
if: matrix.test-type != 'docker'
uses: actions/cache@v4
with:
path: |
.coverage*
htmlcov/
.django_cache/
key: ${{ runner.os }}-django-${{ matrix.test-type }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-django-${{ matrix.test-type }}-
${{ runner.os }}-django-
- name: 安装Python依赖
if: steps.test-info.outputs.skip_python_setup == 'false'
run: |
python -m pip install --upgrade pip
echo "📦 安装Python依赖 (Python ${{ matrix.python-version }})"
python -m pip install --upgrade pip setuptools wheel
# 安装基础依赖
pip install -r requirements.txt
- name: Run Tests
# 根据测试类型安装额外依赖
if [ "${{ matrix.coverage }}" = "true" ]; then
echo "📊 安装覆盖率工具"
pip install coverage[toml]
fi
# 验证关键依赖
echo "🔍 验证关键依赖安装"
python -c "import django; print(f'Django version: {django.get_version()}')"
python -c "import MySQLdb; print('MySQL client: OK')" || python -c "import pymysql; print('PyMySQL client: OK')"
if [ "${{ matrix.elasticsearch }}" = "true" ]; then
python -c "import elasticsearch; print('Elasticsearch client: OK')"
fi
# Django环境准备
- name: 准备Django环境
if: matrix.test-type != 'docker'
env:
DJANGO_MYSQL_PASSWORD: root
DJANGO_MYSQL_HOST: 127.0.0.1
DJANGO_ELASTICSEARCH_HOST: 127.0.0.1:9200
DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }}
run: |
python manage.py makemigrations
python manage.py migrate
coverage run manage.py test
coverage xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build and push
uses: docker/build-push-action@v3
echo "🔧 准备Django测试环境"
# 等待数据库就绪
echo "⏳ 等待MySQL数据库启动..."
for i in {1..30}; do
if python -c "import MySQLdb; MySQLdb.connect(host='127.0.0.1', user='root', passwd='root', db='djangoblog')" 2>/dev/null; then
echo "✅ MySQL数据库连接成功"
break
fi
echo "🔄 等待数据库启动... ($i/30)"
sleep 2
done
# 等待Elasticsearch就绪 (如果启用)
if [ "${{ matrix.elasticsearch }}" = "true" ]; then
echo "⏳ 等待Elasticsearch启动..."
for i in {1..30}; do
if curl -s http://127.0.0.1:9200/_cluster/health | grep -q '"status":"green"\|"status":"yellow"'; then
echo "✅ Elasticsearch连接成功"
break
fi
echo "🔄 等待Elasticsearch启动... ($i/30)"
sleep 2
done
fi
# Django测试执行
- name: 执行数据库迁移
if: matrix.test-type != 'docker'
env:
DJANGO_MYSQL_PASSWORD: root
DJANGO_MYSQL_HOST: 127.0.0.1
DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }}
run: |
echo "🗄️ 执行数据库迁移"
# 检查迁移文件
echo "📋 检查待应用的迁移..."
python manage.py showmigrations
# 检查是否有未创建的迁移
python manage.py makemigrations --check --verbosity 2
# 执行迁移
python manage.py migrate --verbosity 2
echo "✅ 数据库迁移完成"
- name: 运行Django测试
if: matrix.test-type != 'docker'
env:
DJANGO_MYSQL_PASSWORD: root
DJANGO_MYSQL_HOST: 127.0.0.1
DJANGO_ELASTICSEARCH_HOST: ${{ matrix.elasticsearch && '127.0.0.1:9200' || '' }}
run: |
echo "🧪 开始执行 ${{ matrix.test-type }} 测试 (Python ${{ matrix.python-version }})"
# 显示Django配置信息
python manage.py diffsettings | head -20
# 运行测试
if [ "${{ matrix.coverage }}" = "true" ]; then
echo "📊 运行测试并生成覆盖率报告"
coverage run --source='.' --omit='*/venv/*,*/migrations/*,*/tests/*,manage.py' manage.py test --verbosity=2
echo "📈 生成覆盖率报告"
coverage xml
coverage report --show-missing
coverage html
echo "📋 覆盖率统计:"
coverage report | tail -1
else
echo "🧪 运行标准测试"
python manage.py test --verbosity=2 --failfast
fi
echo "✅ 测试执行完成"
# 覆盖率报告上传 (只有完整测试才执行)
- name: 上传覆盖率到Codecov
if: matrix.coverage == true && success()
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
flags: unittests
name: codecov-${{ steps.test-info.outputs.test_name }}
fail_ci_if_error: false
verbose: true
- name: 上传覆盖率到Codecov (备用)
if: matrix.coverage == true && failure()
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
flags: unittests
name: codecov-${{ steps.test-info.outputs.test_name }}-fallback
fail_ci_if_error: false
verbose: true
# Docker构建测试
- name: 设置QEMU
if: matrix.test-type == 'docker'
uses: docker/setup-qemu-action@v3
- name: 设置Docker Buildx
if: matrix.test-type == 'docker'
uses: docker/setup-buildx-action@v3
- name: Docker构建测试
if: matrix.test-type == 'docker'
uses: docker/build-push-action@v5
with:
context: .
push: false
tags: djangoblog/djangoblog:dev
tags: djangoblog/djangoblog:test-${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# 收集测试工件 (失败时收集调试信息)
- name: 收集测试工件
if: failure() && matrix.test-type != 'docker'
run: |
echo "🔍 收集测试失败的调试信息"
# 收集Django日志
if [ -d "logs" ]; then
echo "📄 Django日志文件:"
ls -la logs/
if [ -f "logs/djangoblog.log" ]; then
echo "🔍 最新日志内容:"
tail -100 logs/djangoblog.log
fi
fi
# 显示数据库状态
echo "🗄️ 数据库连接状态:"
python -c "
try:
from django.db import connection
cursor = connection.cursor()
cursor.execute('SELECT VERSION()')
print(f'MySQL版本: {cursor.fetchone()[0]}')
cursor.execute('SHOW TABLES')
tables = cursor.fetchall()
print(f'数据库表数量: {len(tables)}')
except Exception as e:
print(f'数据库连接错误: {e}')
" || true
# Elasticsearch状态 (如果启用)
if [ "${{ matrix.elasticsearch }}" = "true" ]; then
echo "🔍 Elasticsearch状态:"
curl -s http://127.0.0.1:9200/_cluster/health?pretty || true
fi
# 上传测试工件
- name: 上传覆盖率HTML报告
if: matrix.coverage == true && always()
uses: actions/upload-artifact@v4
with:
name: coverage-report-${{ steps.test-info.outputs.test_name }}
path: htmlcov/
retention-days: 30
# 性能统计
- name: 测试性能统计
if: always() && matrix.test-type != 'docker'
run: |
echo "⚡ 测试性能统计:"
echo " 开始时间: $(date -d '@${{ job.started_at }}' '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo '未知')"
echo " 当前时间: $(date '+%Y-%m-%d %H:%M:%S')"
# 系统资源使用情况
echo "💻 系统资源:"
echo " CPU使用: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)%"
echo " 内存使用: $(free -h | awk '/^Mem:/ {printf "%.1f%%", $3/$2 * 100}')"
echo " 磁盘使用: $(df -h / | awk 'NR==2{printf "%s", $5}')"
# 测试结果汇总
- name: 测试完成总结
if: always()
run: |
echo "📋 ============ 测试执行总结 ============"
echo " 🏷️ 测试类型: ${{ matrix.test-type }}"
echo " 🐍 Python版本: ${{ matrix.python-version }}"
echo " 🗄️ 数据库: ${{ matrix.database }}"
echo " 🔍 Elasticsearch: ${{ matrix.elasticsearch }}"
echo " 📊 覆盖率: ${{ matrix.coverage }}"
echo " ⚡ 状态: ${{ job.status }}"
echo " 📅 完成时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "============================================"
# 根据测试结果显示不同消息
if [ "${{ job.status }}" = "success" ]; then
echo "🎉 测试执行成功!"
else
echo "❌ 测试执行失败,请检查上面的日志"
fi

@ -22,19 +22,19 @@ jobs:
run: |
echo "DOCKER_TAG=latest" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
context: .
push: true

2
src/.gitignore vendored

@ -62,10 +62,10 @@ target/
# http://www.jetbrains.com/pycharm/webhelp/project.html
.idea
.iml
static/
# virtualenv
venv/
collectedstatic/
djangoblog/whoosh_index/
google93fd32dbd906620a.html
baidu_verify_FlHL7cUyC9.html

@ -90,6 +90,7 @@ python manage.py migrate
# 创建一个超级管理员账户
python manage.py createsuperuser
--zyd202020 zbzydzfy123456
```
### 5. 运行项目
@ -146,7 +147,7 @@ python manage.py runserver
## 🙏 鸣谢
特别感谢 **JetBrains** 为本项目提供的免费开源许可证。gs ls syj zyd164
特别感谢 **JetBrains** 为本项目提供的免费开源许可证。
<p align="center">
<a href="https://www.jetbrains.com/?from=DjangoBlog">

@ -57,3 +57,22 @@ class BlogUserAdmin(UserAdmin):
'source')
list_display_links = ('id', 'username')
ordering = ('-id',)
search_fields = ('username', 'nickname', 'email')
# 修复字段集配置
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email', 'nickname')}),
(_('Permissions'), {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions'),
}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
(_('Additional info'), {'fields': ('source', 'creation_time', 'last_modify_time')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'password1', 'password2'),
}),
)

@ -56,7 +56,7 @@ class AccountTest(TestCase):
self.assertEqual(response.status_code, 200)
def test_validate_register(self):
self.assertEquals(
self.assertEqual(
0, len(
BlogUser.objects.filter(
email='user123@user.com')))
@ -66,7 +66,7 @@ class AccountTest(TestCase):
'password1': 'password123!q@wE#R$T',
'password2': 'password123!q@wE#R$T',
})
self.assertEquals(
self.assertEqual(
1, len(
BlogUser.objects.filter(
email='user123@user.com')))
@ -205,3 +205,31 @@ class AccountTest(TestCase):
self.assertEqual(resp.status_code, 200)
def test_favorites_page(self):
user = BlogUser.objects.create_superuser(
email="fav@user.com",
username="favuser",
password="favpass")
category = Category()
category.name = "favcategory"
category.save()
article = Article()
article.title = "favorite title"
article.body = "favorite body"
article.author = user
article.category = category
article.type = 'a'
article.status = 'p'
article.save()
# 收藏
article.favorite_users.add(user)
# 登录并访问收藏页
self.client.login(username='favuser', password='favpass')
resp = self.client.get(reverse('account:favorites'))
self.assertEqual(resp.status_code, 200)
self.assertContains(resp, "favorite title")

@ -25,4 +25,5 @@ urlpatterns = [re_path(r'^login/$',
re_path(r'^forget_password_code/$',
views.ForgetPasswordEmailCode.as_view(),
name='forget_password_code'),
path('favorites/', views.FavoriteArticlesView.as_view(), name='favorites'),
]

@ -20,6 +20,8 @@ from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import FormView, RedirectView
from django.views.generic.list import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from djangoblog.utils import send_email, get_sha256, get_current_site, generate_code, delete_sidebar_cache
from . import utils
@ -202,3 +204,20 @@ class ForgetPasswordEmailCode(View):
utils.set_code(to_email, code)
return HttpResponse("ok")
class FavoriteArticlesView(LoginRequiredMixin, ListView):
template_name = 'blog/article_index.html'
context_object_name = 'article_list'
paginate_by = settings.PAGINATE_BY if hasattr(settings, 'PAGINATE_BY') else 10
def get_queryset(self):
return self.request.user.favorite_articles.filter(status='p').order_by('-pub_time')
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['page_type'] = '我的收藏'
ctx['tag_name'] = None
ctx['linktype'] = 'i'
ctx['sort'] = 'latest'
return ctx

@ -6,7 +6,7 @@ from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
# Register your models here.
from .models import Article
from .models import Article, Category, Tag, Links, SideBar, BlogSettings
class ArticleForm(forms.ModelForm):
@ -55,6 +55,7 @@ class ArticlelAdmin(admin.ModelAdmin):
'article_order')
list_display_links = ('id', 'title')
list_filter = ('status', 'type', 'category')
date_hierarchy = 'creation_time'
filter_horizontal = ('tags',)
exclude = ('creation_time', 'last_modify_time')
view_on_site = True
@ -63,6 +64,7 @@ class ArticlelAdmin(admin.ModelAdmin):
draft_article,
close_article_commentstatus,
open_article_commentstatus]
raw_id_fields = ('author', 'category',)
def link_to_category(self, obj):
info = (obj.category._meta.app_label, obj.category._meta.model_name)

@ -9,8 +9,16 @@ class Command(BaseCommand):
help = 'create test datas'
def handle(self, *args, **options):
user = get_user_model().objects.get_or_create(
email='test@test.com', username='测试用户', password=make_password('test!q@w#eTYU'))[0]
# 先尝试通过用户名查找用户
try:
user = get_user_model().objects.get(username='测试用户')
except get_user_model().DoesNotExist:
# 如果用户不存在,则创建新用户
user = get_user_model().objects.create(
email='test@test.com',
username='测试用户',
password=make_password('test!q@w#eTYU')
)
pcategory = Category.objects.get_or_create(
name='我是父类目', parent_category=None)[0]
@ -19,21 +27,26 @@ class Command(BaseCommand):
name='子类目', parent_category=pcategory)[0]
category.save()
basetag = Tag()
basetag.name = "标签"
basetag.save()
# 使用 get_or_create 处理基础标签
basetag, created = Tag.objects.get_or_create(name="标签")
for i in range(1, 20):
article = Article.objects.get_or_create(
article, article_created = Article.objects.get_or_create(
category=category,
title='nice title ' + str(i),
body='nice content ' + str(i),
author=user)[0]
tag = Tag()
tag.name = "标签" + str(i)
tag.save()
article.tags.add(tag)
article.tags.add(basetag)
article.save()
author=user
)
# 使用 get_or_create 处理每个标签
tag, tag_created = Tag.objects.get_or_create(name="标签" + str(i))
# 只有在文章是新创建的时候才添加标签
if article_created:
article.tags.add(tag)
article.tags.add(basetag)
article.save()
from djangoblog.utils import cache
cache.clear()

@ -0,0 +1,33 @@
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('blog', '0006_alter_blogsettings_options'),
]
operations = [
migrations.AddField(
model_name='article',
name='like_count',
field=models.PositiveIntegerField(default=0, verbose_name='likes'),
),
migrations.AddField(
model_name='article',
name='favorite_count',
field=models.PositiveIntegerField(default=0, verbose_name='favorites'),
),
migrations.AddField(
model_name='article',
name='like_users',
field=models.ManyToManyField(blank=True, related_name='liked_articles', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='article',
name='favorite_users',
field=models.ManyToManyField(blank=True, related_name='favorite_articles', to=settings.AUTH_USER_MODEL),
),
]

@ -88,6 +88,18 @@ class Article(BaseModel):
default='o')
type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a')
views = models.PositiveIntegerField(_('views'), default=0)
like_count = models.PositiveIntegerField('likes', default=0)
favorite_count = models.PositiveIntegerField('favorites', default=0)
like_users = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name='liked_articles',
blank=True
)
favorite_users = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name='favorite_articles',
blank=True
)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=_('author'),
@ -139,6 +151,26 @@ class Article(BaseModel):
self.views += 1
self.save(update_fields=['views'])
def like_once_by_user(self, user):
if not user.is_authenticated:
return False
if self.like_users.filter(pk=user.pk).exists():
return False
self.like_users.add(user)
self.like_count += 1
self.save(update_fields=['like_count'])
return True
def favorite_once_by_user(self, user):
if not user.is_authenticated:
return False
if self.favorite_users.filter(pk=user.pk).exists():
return False
self.favorite_users.add(user)
self.favorite_count += 1
self.save(update_fields=['favorite_count'])
return True
def comment_list(self):
cache_key = 'article_comments_{id}'.format(id=self.id)
value = cache.get(cache_key)

@ -0,0 +1,9 @@
.button {
border: none;
padding: 4px 80px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
}

@ -0,0 +1,47 @@
let wait = 60;
function time(o) {
if (wait == 0) {
o.removeAttribute("disabled");
o.value = "获取验证码";
wait = 60
return false
} else {
o.setAttribute("disabled", true);
o.value = "重新发送(" + wait + ")";
wait--;
setTimeout(function () {
time(o)
},
1000)
}
}
document.getElementById("btn").onclick = function () {
let id_email = $("#id_email")
let token = $("*[name='csrfmiddlewaretoken']").val()
let ts = this
let myErr = $("#myErr")
$.ajax(
{
url: "/forget_password_code/",
type: "POST",
data: {
"email": id_email.val(),
"csrfmiddlewaretoken": token
},
success: function (result) {
if (result != "ok") {
myErr.remove()
id_email.after("<ul className='errorlist' id='myErr'><li>" + result + "</li></ul>")
return
}
myErr.remove()
time(ts)
},
error: function (e) {
alert("发送失败,请重试")
}
}
);
}

@ -0,0 +1,58 @@
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #fff;
}
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-signin-heading {
margin: 0 0 15px;
font-size: 18px;
font-weight: 400;
color: #555;
}
.form-signin .checkbox {
margin-bottom: 10px;
font-weight: normal;
}
.form-signin .form-control {
position: relative;
height: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: 10px;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
}
.card {
width: 304px;
padding: 20px 25px 30px;
margin: 0 auto 25px;
background-color: #f7f7f7;
border-radius: 2px;
-webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
box-shadow: 0 2px 2px rgba(0, 0, 0, .3);
}
.card-signin {
width: 354px;
padding: 40px;
}
.card-signin .profile-img {
display: block;
width: 96px;
height: 96px;
margin: 0 auto 10px;
}

Before

Width:  |  Height:  |  Size: 221 B

After

Width:  |  Height:  |  Size: 221 B

@ -1,18 +1,13 @@
/*
IEIE9
Styles for older IE versions (previous to IE9).
*/
/* 页面主体背景色 */
body {
background-color: #e6e6e6;
}
/* 自定义背景为空时的样式 */
body.custom-background-empty {
background-color: #fff;
}
/* 自定义背景为空或白色时站点样式 */
body.custom-background-empty .site,
body.custom-background-white .site {
box-shadow: none;
@ -20,20 +15,14 @@ body.custom-background-white .site {
margin-top: 0;
padding: 0;
}
/* 辅助文本和屏幕阅读器文本 */
.assistive-text,
.site .screen-reader-text {
clip: rect(1px 1px 1px 1px);
}
/* 全宽页面内容 */
.full-width .site-content {
float: none;
width: 100%;
}
/* 防止IE8中具有高度和宽度属性的完整尺寸和大尺寸图像被拉伸 */
img.size-full,
img.size-large,
img.header-image,
@ -41,23 +30,17 @@ img.wp-post-image,
img[class*="align"],
img[class*="wp-image-"],
img[class*="attachment-"] {
width: auto; /* 防止在IE8中拉伸完整尺寸和大尺寸图像 */
width: auto; /* Prevent stretching of full-size and large-size images with height and width attributes in IE8 */
}
/* 作者头像 */
.author-avatar {
float: left;
margin-top: 8px;
margin-top: 0.571428571rem;
}
/* 作者描述 */
.author-description {
float: right;
width: 80%;
}
/* 站点容器 */
.site {
box-shadow: 0 2px 6px rgba(100, 100, 100, 0.3);
margin: 48px auto;
@ -65,39 +48,27 @@ img[class*="attachment-"] {
overflow: hidden;
padding: 0 40px;
}
/* 站点内容 */
.site-content {
float: left;
width: 65.104166667%;
}
/* 不同页面模板的站点内容宽度 */
body.template-front-page .site-content,
body.attachment .site-content,
body.full-width .site-content {
width: 100%;
}
/* 小工具区域 */
.widget-area {
float: right;
width: 26.041666667%;
}
/* 站点头部标题 */
.site-header h1,
.site-header h2 {
text-align: left;
}
/* 站点头部一级标题 */
.site-header h1 {
font-size: 26px;
line-height: 1.846153846;
}
/* 主导航菜单 */
.main-navigation ul.nav-menu,
.main-navigation div.nav-menu > ul {
border-bottom: 1px solid #ededed;
@ -106,46 +77,32 @@ body.full-width .site-content {
text-align: left;
width: 100%;
}
/* 主导航菜单列表 */
.main-navigation ul {
margin: 0;
text-indent: 0;
}
/* 主导航菜单项和链接 */
.main-navigation li a,
.main-navigation li {
display: inline-block;
text-decoration: none;
}
/* IE7浏览器特殊处理 */
.ie7 .main-navigation li a,
.ie7 .main-navigation li {
display: inline;
}
/* 主导航菜单链接 */
.main-navigation li a {
border-bottom: 0;
color: #6a6a6a;
line-height: 3.692307692;
text-transform: uppercase;
}
/* 主导航菜单链接悬停效果 */
.main-navigation li a:hover {
color: #000;
}
/* 主导航菜单项 */
.main-navigation li {
margin: 0 40px 0 0;
position: relative;
}
/* 主导航菜单下拉列表 */
.main-navigation li ul {
margin: 0;
padding: 0;
@ -157,23 +114,17 @@ body.full-width .site-content {
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
/* IE7浏览器下拉列表特殊处理 */
.ie7 .main-navigation li ul {
clip: inherit;
display: none;
left: 0;
overflow: visible;
}
/* 多级下拉菜单 */
.main-navigation li ul ul,
.ie7 .main-navigation li ul ul {
top: 0;
left: 100%;
}
/* 下拉菜单显示 */
.main-navigation ul li:hover > ul,
.main-navigation ul li:focus > ul,
.main-navigation .focus > ul {
@ -183,14 +134,10 @@ body.full-width .site-content {
height: inherit;
width: inherit;
}
/* IE7浏览器下拉菜单显示特殊处理 */
.ie7 .main-navigation ul li:hover > ul,
.ie7 .main-navigation ul li:focus > ul {
display: block;
}
/* 下拉菜单项链接 */
.main-navigation li ul li a {
background: #efefef;
border-bottom: 1px solid #ededed;
@ -200,14 +147,10 @@ body.full-width .site-content {
padding: 8px 10px;
width: 180px;
}
/* 下拉菜单项链接悬停效果 */
.main-navigation li ul li a:hover {
background: #e3e3e3;
color: #444;
}
/* 当前菜单项样式 */
.main-navigation .current-menu-item > a,
.main-navigation .current-menu-ancestor > a,
.main-navigation .current_page_item > a,
@ -215,58 +158,39 @@ body.full-width .site-content {
color: #636363;
font-weight: bold;
}
/* 菜单切换按钮 */
.main-navigation .menu-toggle {
display: none;
}
/* 文章标题 */
.entry-header .entry-title {
font-size: 22px;
}
/* 评论表单输入框 */
#respond form input[type="text"] {
width: 46.333333333%;
}
/* 评论表单文本域 */
#respond form textarea.blog-textarea {
width: 79.666666667%;
}
/* 首页模板内容 */
.template-front-page .site-content,
.template-front-page article {
overflow: hidden;
}
/* 首页模板带缩略图文章 */
.template-front-page.has-post-thumbnail article {
float: left;
width: 47.916666667%;
}
/* 页面图像 */
.entry-page-image {
float: right;
margin-bottom: 0;
width: 47.916666667%;
}
/* IE首页模板小工具修复 */
/* IE Front Page Template Widget fix */
.template-front-page .widget-area {
clear: both;
}
/* 首页模板小工具 */
.template-front-page .widget {
width: 100% !important;
border: none;
}
/* 首页模板小工具区域 */
.template-front-page .widget-area .widget,
.template-front-page .first.front-widgets,
.template-front-page.two-sidebars .widget-area .front-widgets {
@ -274,14 +198,10 @@ body.full-width .site-content {
margin-bottom: 24px;
width: 51.875%;
}
/* 首页模板第二组小工具 */
.template-front-page .second.front-widgets,
.template-front-page .widget-area .widget:nth-child(odd) {
clear: right;
}
/* 首页模板第一组和第二组小工具 */
.template-front-page .first.front-widgets,
.template-front-page .second.front-widgets,
.template-front-page.two-sidebars .widget-area .front-widgets + .front-widgets {
@ -289,88 +209,64 @@ body.full-width .site-content {
margin: 0 0 24px;
width: 39.0625%;
}
/* 双侧边栏模板小工具 */
.template-front-page.two-sidebars .widget,
.template-front-page.two-sidebars .widget:nth-child(even) {
float: none;
width: auto;
}
/* 为IE9之前的密码框添加字体以使圆点显示 */
/* add input font for <IE9 Password Box to make the bullets show up */
input[type="password"] {
font-family: Helvetica, Arial, sans-serif;
}
/* IE7IE8RTL
/* RTL overrides for IE7 and IE8
-------------------------------------------------------------- */
/* RTL布局站点标题 */
.rtl .site-header h1,
.rtl .site-header h2 {
text-align: right;
}
/* RTL布局小工具区域和作者描述 */
.rtl .widget-area,
.rtl .author-description {
float: left;
}
/* RTL布局作者头像和站点内容 */
.rtl .author-avatar,
.rtl .site-content {
float: right;
}
/* RTL布局导航菜单 */
.rtl .main-navigation ul.nav-menu,
.rtl .main-navigation div.nav-menu > ul {
text-align: right;
}
/* RTL布局多级导航菜单项 */
.rtl .main-navigation ul li ul li,
.rtl .main-navigation ul li ul li ul li {
margin-left: 40px;
margin-right: auto;
}
/* RTL布局三级导航菜单 */
.rtl .main-navigation li ul ul {
position: absolute;
bottom: 0;
right: 100%;
z-index: 1;
}
/* IE7浏览器RTL布局三级导航菜单特殊处理 */
.ie7 .rtl .main-navigation li ul ul {
position: absolute;
bottom: 0;
right: 100%;
z-index: 1;
}
/* IE7浏览器RTL布局导航菜单项 */
.ie7 .rtl .main-navigation ul li {
z-index: 99;
}
/* IE7浏览器RTL布局下拉菜单 */
.ie7 .rtl .main-navigation li ul {
position: absolute;
bottom: 100%;
right: 0;
z-index: 1;
}
/* IE7浏览器RTL布局导航菜单项外边距 */
.ie7 .rtl .main-navigation li {
margin-right: auto;
margin-left: 40px;
}
/* IE7浏览器RTL布局四级导航菜单 */
.ie7 .rtl .main-navigation li ul ul ul {
position: relative;
z-index: 1;

@ -1,11 +1,10 @@
/* 使点击事件穿透 */
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
/* 进度条样式 */
#nprogress .bar {
background: red; /* 进度条颜色 */
background: red;
position: fixed;
z-index: 1031;
@ -16,7 +15,7 @@
height: 2px;
}
/* 花哨的模糊效果 */
/* Fancy blur effect */
#nprogress .peg {
display: block;
position: absolute;
@ -31,7 +30,7 @@
transform: rotate(3deg) translate(0px, -4px);
}
/* 移除这些来去掉旋转器 */
/* Remove these to get rid of the spinner */
#nprogress .spinner {
display: block;
position: fixed;
@ -40,7 +39,6 @@
right: 15px;
}
/* 旋转器图标样式 */
#nprogress .spinner-icon {
width: 18px;
height: 18px;
@ -55,26 +53,22 @@
animation: nprogress-spinner 400ms linear infinite;
}
/* 自定义父容器样式 */
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
/* 自定义父容器中的旋转器和进度条样式 */
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
/* Webkit内核浏览器的旋转器动画 */
@-webkit-keyframes nprogress-spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
/* 标准旋转器动画 */
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
}

@ -1,212 +1,173 @@
/* Google图标样式 */
.icon-sn-google {
background-position: 0 -28px;
}
/* Google背景图标样式 */
.icon-sn-bg-google {
background-color: #4285f4;
background-position: 0 0;
}
/* Google字体图标颜色 */
.fa-sn-google {
color: #4285f4;
}
/* GitHub图标样式 */
.icon-sn-github {
background-position: -28px -28px;
}
/* GitHub背景图标样式 */
.icon-sn-bg-github {
background-color: #333;
background-position: -28px 0;
}
/* GitHub字体图标颜色 */
.fa-sn-github {
color: #333;
}
/* 微博图标样式 */
.icon-sn-weibo {
background-position: -56px -28px;
}
/* 微博背景图标样式 */
.icon-sn-bg-weibo {
background-color: #e90d24;
background-position: -56px 0;
}
/* 微博字体图标颜色 */
.fa-sn-weibo {
color: #e90d24;
}
/* QQ图标样式 */
.icon-sn-qq {
background-position: -84px -28px;
}
/* QQ背景图标样式 */
.icon-sn-bg-qq {
background-color: #0098e6;
background-position: -84px 0;
}
/* QQ字体图标颜色 */
.fa-sn-qq {
color: #0098e6;
}
/* Twitter图标样式 */
.icon-sn-twitter {
background-position: -112px -28px;
}
/* Twitter背景图标样式 */
.icon-sn-bg-twitter {
background-color: #50abf1;
background-position: -112px 0;
}
/* Twitter字体图标颜色 */
.fa-sn-twitter {
color: #50abf1;
}
/* Facebook图标样式 */
.icon-sn-facebook {
background-position: -140px -28px;
}
/* Facebook背景图标样式 */
.icon-sn-bg-facebook {
background-color: #4862a3;
background-position: -140px 0;
}
/* Facebook字体图标颜色 */
.fa-sn-facebook {
color: #4862a3;
}
/* 人人网图标样式 */
.icon-sn-renren {
background-position: -168px -28px;
}
/* 人人网背景图标样式 */
.icon-sn-bg-renren {
background-color: #197bc8;
background-position: -168px 0;
}
/* 人人网字体图标颜色 */
.fa-sn-renren {
color: #197bc8;
}
/* 腾讯微博图标样式 */
.icon-sn-tqq {
background-position: -196px -28px;
}
/* 腾讯微博背景图标样式 */
.icon-sn-bg-tqq {
background-color: #1f9ed2;
background-position: -196px 0;
}
/* 腾讯微博字体图标颜色 */
.fa-sn-tqq {
color: #1f9ed2;
}
/* 豆瓣图标样式 */
.icon-sn-douban {
background-position: -224px -28px;
}
/* 豆瓣背景图标样式 */
.icon-sn-bg-douban {
background-color: #279738;
background-position: -224px 0;
}
/* 豆瓣字体图标颜色 */
.fa-sn-douban {
color: #279738;
}
/* 微信图标样式 */
.icon-sn-weixin {
background-position: -252px -28px;
}
/* 微信背景图标样式 */
.icon-sn-bg-weixin {
background-color: #00b500;
background-position: -252px 0;
}
/* 微信字体图标颜色 */
.fa-sn-weixin {
color: #00b500;
}
/* 虚线图标样式 */
.icon-sn-dotted {
background-position: -280px -28px;
}
/* 虚线背景图标样式 */
.icon-sn-bg-dotted {
background-color: #eee;
background-position: -280px 0;
}
/* 虚线字体图标颜色 */
.fa-sn-dotted {
color: #eee;
}
/* 网站图标样式 */
.icon-sn-site {
background-position: -308px -28px;
}
/* 网站背景图标样式 */
.icon-sn-bg-site {
background-color: #00b500;
background-position: -308px 0;
}
/* 网站字体图标颜色 */
.fa-sn-site {
color: #00b500;
}
/* LinkedIn图标样式 */
.icon-sn-linkedin {
background-position: -336px -28px;
}
/* LinkedIn背景图标样式 */
.icon-sn-bg-linkedin {
background-color: #0077b9;
background-position: -336px 0;
}
/* LinkedIn字体图标颜色 */
.fa-sn-linkedin {
color: #0077b9;
}
/* 社交网络图标通用样式 */
[class*=icon-sn-] {
display: inline-block;
background-image: url('../img/icon-sn.svg');
@ -217,159 +178,128 @@
background-size: auto 56px;
}
/* 社交网络图标悬停效果 */
[class*=icon-sn-]:hover {
opacity: .8;
filter: alpha(opacity=80);
}
/* Google按钮样式 */
.btn-sn-google {
background: #4285f4;
}
/* Google按钮激活、聚焦、悬停样式 */
.btn-sn-google:active, .btn-sn-google:focus, .btn-sn-google:hover {
background: #2a75f3;
}
/* GitHub按钮样式 */
.btn-sn-github {
background: #333;
}
/* GitHub按钮激活、聚焦、悬停样式 */
.btn-sn-github:active, .btn-sn-github:focus, .btn-sn-github:hover {
background: #262626;
}
/* 微博按钮样式 */
.btn-sn-weibo {
background: #e90d24;
}
/* 微博按钮激活、聚焦、悬停样式 */
.btn-sn-weibo:active, .btn-sn-weibo:focus, .btn-sn-weibo:hover {
background: #d10c20;
}
/* QQ按钮样式 */
.btn-sn-qq {
background: #0098e6;
}
/* QQ按钮激活、聚焦、悬停样式 */
.btn-sn-qq:active, .btn-sn-qq:focus, .btn-sn-qq:hover {
background: #0087cd;
}
/* Twitter按钮样式 */
.btn-sn-twitter {
background: #50abf1;
}
/* Twitter按钮激活、聚焦、悬停样式 */
.btn-sn-twitter:active, .btn-sn-twitter:focus, .btn-sn-twitter:hover {
background: #38a0ef;
}
/* Facebook按钮样式 */
.btn-sn-facebook {
background: #4862a3;
}
/* Facebook按钮激活、聚焦、悬停样式 */
.btn-sn-facebook:active, .btn-sn-facebook:focus, .btn-sn-facebook:hover {
background: #405791;
}
/* 人人网按钮样式 */
.btn-sn-renren {
background: #197bc8;
}
/* 人人网按钮激活、聚焦、悬停样式 */
.btn-sn-renren:active, .btn-sn-renren:focus, .btn-sn-renren:hover {
background: #166db1;
}
/* 腾讯微博按钮样式 */
.btn-sn-tqq {
background: #1f9ed2;
}
/* 腾讯微博按钮激活、聚焦、悬停样式 */
.btn-sn-tqq:active, .btn-sn-tqq:focus, .btn-sn-tqq:hover {
background: #1c8dbc;
}
/* 豆瓣按钮样式 */
.btn-sn-douban {
background: #279738;
}
/* 豆瓣按钮激活、聚焦、悬停样式 */
.btn-sn-douban:active, .btn-sn-douban:focus, .btn-sn-douban:hover {
background: #228330;
}
/* 微信按钮样式 */
.btn-sn-weixin {
background: #00b500;
}
/* 微信按钮激活、聚焦、悬停样式 */
.btn-sn-weixin:active, .btn-sn-weixin:focus, .btn-sn-weixin:hover {
background: #009c00;
}
/* 虚线按钮样式 */
.btn-sn-dotted {
background: #eee;
}
/* 虚线按钮激活、聚焦、悬停样式 */
.btn-sn-dotted:active, .btn-sn-dotted:focus, .btn-sn-dotted:hover {
background: #e1e1e1;
}
/* 网站按钮样式 */
.btn-sn-site {
background: #00b500;
}
/* 网站按钮激活、聚焦、悬停样式 */
.btn-sn-site:active, .btn-sn-site:focus, .btn-sn-site:hover {
background: #009c00;
}
/* LinkedIn按钮样式 */
.btn-sn-linkedin {
background: #0077b9;
}
/* LinkedIn按钮激活、聚焦、悬停样式 */
.btn-sn-linkedin:active, .btn-sn-linkedin:focus, .btn-sn-linkedin:hover {
background: #0067a0;
}
/* 社交网络按钮通用样式 */
[class*=btn-sn-], [class*=btn-sn-]:active, [class*=btn-sn-]:focus, [class*=btn-sn-]:hover {
border: none;
color: #fff;
}
/* 更多按钮样式 */
.btn-sn-more {
padding: 0;
}
/* 更多按钮及激活、悬停样式 */
.btn-sn-more, .btn-sn-more:active, .btn-sn-more:hover {
box-shadow: none;
}
/* 社交网络按钮内的图标样式 */
[class*=btn-sn-] [class*=icon-sn-] {
background-color: transparent;
}

@ -0,0 +1,600 @@
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtE6F15M.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWvU6F15M.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtU6F15M.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuk6F15M.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWu06F15M.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWxU6F15M.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqW106F15M.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtk6F15M.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWt06F15M.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuU6F.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtE6F15M.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWvU6F15M.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtU6F15M.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuk6F15M.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWu06F15M.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWxU6F15M.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqW106F15M.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtk6F15M.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWt06F15M.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuU6F.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtE6F15M.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWvU6F15M.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtU6F15M.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuk6F15M.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWu06F15M.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWxU6F15M.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqW106F15M.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWtk6F15M.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWt06F15M.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memtYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWqWuU6F.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
font-display: swap;
src: url(memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

@ -1,62 +1,40 @@
/**
* 博客前端交互功能
* Created by liangliang on 2016/11/20.
*/
/**
* 回复评论功能
* @param {string} parentid - 父评论ID
*/
function do_reply(parentid) {
console.log(parentid);
// 设置父评论ID
$("#id_parent_comment_id").val(parentid)
// 将评论表单移动到对应评论下
$("#commentform").appendTo($("#div-comment-" + parentid));
// 隐藏回复标题,显示取消回复按钮
$("#reply-title").hide();
$("#cancel_comment").show();
}
/**
* 取消回复功能
*/
function cancel_reply() {
// 显示回复标题,隐藏取消回复按钮
$("#reply-title").show();
$("#cancel_comment").hide();
// 清空父评论ID
$("#id_parent_comment_id").val('')
// 将评论表单移回原位置
$("#commentform").appendTo($("#respond"));
}
// 页面加载进度条控制
NProgress.start();
NProgress.set(0.4);
// 增量更新进度条
//Increment
var interval = setInterval(function () {
NProgress.inc();
}, 1000);
// 文档加载完成后停止进度条
$(document).ready(function () {
NProgress.done();
clearInterval(interval);
});
/** 侧边栏回到顶部功能 */
/** 侧边栏回到顶部 */
var rocket = $('#rocket');
// 监听窗口滚动事件,防抖处理
$(window).on('scroll', debounce(slideTopSet, 300));
/**
* 防抖函数
* @param {Function} func - 需要防抖的函数
* @param {number} wait - 等待时间毫秒
* @returns {Function} 防抖后的函数
*/
function debounce(func, wait) {
var timeout;
return function () {
@ -65,54 +43,45 @@ function debounce(func, wait) {
};
}
/**
* 设置回到顶部按钮显示状态
*/
function slideTopSet() {
var top = $(document).scrollTop();
if (top > 200) {
rocket.addClass('show'); // 滚动超过200px时显示回到顶部按钮
rocket.addClass('show');
} else {
rocket.removeClass('show'); // 否则隐藏
rocket.removeClass('show');
}
}
// 点击回到顶部按钮事件
$(document).on('click', '#rocket', function (event) {
rocket.addClass('move'); // 添加动画类
// 页面平滑滚动到顶部
rocket.addClass('move');
$('body, html').animate({
scrollTop: 0
}, 800);
});
// 动画结束事件处理
$(document).on('animationEnd', function () {
setTimeout(function () {
rocket.removeClass('move'); // 移除动画类
rocket.removeClass('move');
}, 400);
});
// Webkit内核浏览器动画结束事件处理
});
$(document).on('webkitAnimationEnd', function () {
setTimeout(function () {
rocket.removeClass('move'); // 移除动画类
rocket.removeClass('move');
}, 400);
});
// 页面加载完成后初始化评论回复功能
window.onload = function () {
var replyLinks = document.querySelectorAll(".comment-reply-link");
for (var i = 0; i < replyLinks.length; i++) {
replyLinks[i].onclick = function () {
var pk = this.getAttribute("data-pk");
do_reply(pk); // 点击回复链接时调用回复功能
do_reply(pk);
};
}
};
// 国际化语言切换功能(已注释)
// $(document).ready(function () {
// var form = $('#i18n-form');
// var selector = $('.i18n-select');

@ -0,0 +1,142 @@
/**
* MathJax 智能加载器
* 检测页面是否包含数学公式如果有则动态加载和配置MathJax
*/
(function() {
'use strict';
/**
* 检测页面是否包含数学公式
* @returns {boolean} 是否包含数学公式
*/
function hasMathFormulas() {
const content = document.body.textContent || document.body.innerText || '';
// 检测常见的数学公式语法
return /\$.*?\$|\$\$.*?\$\$|\\begin\{.*?\}|\\end\{.*?\}|\\[a-zA-Z]+\{/.test(content);
}
/**
* 配置MathJax
*/
function configureMathJax() {
window.MathJax = {
tex: {
// 行内公式和块级公式分隔符
inlineMath: [['$', '$']],
displayMath: [['$$', '$$']],
// 处理转义字符和LaTeX环境
processEscapes: true,
processEnvironments: true,
// 自动换行
tags: 'ams'
},
options: {
// 跳过这些HTML标签避免处理代码块等
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code', 'a'],
// CSS类控制
ignoreHtmlClass: 'tex2jax_ignore',
processHtmlClass: 'tex2jax_process'
},
// 启动配置
startup: {
ready() {
console.log('MathJax配置完成开始初始化...');
MathJax.startup.defaultReady();
// 处理特定区域的数学公式
const contentEl = document.getElementById('content');
const commentsEl = document.getElementById('comments');
const promises = [];
if (contentEl) {
promises.push(MathJax.typesetPromise([contentEl]));
}
if (commentsEl) {
promises.push(MathJax.typesetPromise([commentsEl]));
}
// 等待所有渲染完成
Promise.all(promises).then(() => {
console.log('MathJax渲染完成');
// 触发自定义事件通知其他脚本MathJax已就绪
document.dispatchEvent(new CustomEvent('mathjaxReady'));
}).catch(error => {
console.error('MathJax渲染失败:', error);
});
}
},
// 输出配置
chtml: {
scale: 1,
minScale: 0.5,
matchFontHeight: false,
displayAlign: 'center',
displayIndent: '0'
}
};
}
/**
* 加载MathJax库
*/
function loadMathJax() {
console.log('检测到数学公式开始加载MathJax...');
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
script.async = true;
script.defer = true;
script.onload = function() {
console.log('MathJax库加载成功');
};
script.onerror = function() {
console.error('MathJax库加载失败尝试备用CDN...');
// 备用CDN
const fallbackScript = document.createElement('script');
fallbackScript.src = 'https://polyfill.io/v3/polyfill.min.js?features=es6';
fallbackScript.onload = function() {
const mathJaxScript = document.createElement('script');
mathJaxScript.src = 'https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML';
mathJaxScript.async = true;
document.head.appendChild(mathJaxScript);
};
document.head.appendChild(fallbackScript);
};
document.head.appendChild(script);
}
/**
* 初始化函数
*/
function init() {
// 等待DOM完全加载
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
return;
}
// 检测是否需要加载MathJax
if (hasMathFormulas()) {
// 先配置,再加载
configureMathJax();
loadMathJax();
} else {
console.log('未检测到数学公式跳过MathJax加载');
}
}
// 提供重新渲染的全局方法,供动态内容使用
window.rerenderMathJax = function(element) {
if (window.MathJax && window.MathJax.typesetPromise) {
const target = element || document.body;
return window.MathJax.typesetPromise([target]);
}
return Promise.resolve();
};
// 启动初始化
init();
})();

@ -1,5 +1,6 @@
/**
* 处理小屏幕上的导航菜单切换和子菜单项的可访问性
* Handles toggling the navigation menu for small screens and
* accessibility for submenu items.
*/
( function() {
var nav = document.getElementById( 'site-navigation' ), button, menu;
@ -13,19 +14,17 @@
return;
}
// 如果菜单不存在或为空则隐藏按钮
// Hide button if menu is missing or empty.
if ( ! menu || ! menu.childNodes.length ) {
button.style.display = 'none';
return;
}
// 按钮点击事件处理
button.onclick = function() {
if ( -1 === menu.className.indexOf( 'nav-menu' ) ) {
menu.className = 'nav-menu';
}
// 切换菜单展开/收起状态
if ( -1 !== button.className.indexOf( 'toggled-on' ) ) {
button.className = button.className.replace( ' toggled-on', '' );
menu.className = menu.className.replace( ' toggled-on', '' );
@ -36,14 +35,12 @@
};
} )();
// 为隐藏的子菜单项提供更好的焦点支持以提高可访问性
// Better focus for hidden submenu items for accessibility.
( function( $ ) {
// 为导航菜单中的链接添加焦点和失焦事件
$( '.main-navigation' ).find( 'a' ).on( 'focus.twentytwelve blur.twentytwelve', function() {
$( this ).parents( '.menu-item, .page_item' ).toggleClass( 'focus' );
} );
// 移动端触摸事件处理
if ( 'ontouchstart' in window ) {
$('body').on( 'touchstart.twentytwelve', '.menu-item-has-children > a, .page_item_has_children > a', function( e ) {
var el = $( this ).parent( 'li' );
@ -55,4 +52,4 @@
}
} );
}
} )( jQuery );
} )( jQuery );

@ -1,352 +1,293 @@
/* 代码高亮样式 */
.codehilite .hll {
background-color: #ffffcc
}
/* 代码块背景 */
.codehilite {
background: #ffffff;
}
/* 注释 */
.codehilite .c {
color: #177500
}
/* 注释 */
/* Error */
/* Comment */
.codehilite .err {
color: #000000
}
/* 错误 */
/* Keyword */
/* Error */
.codehilite .k {
color: #A90D91
}
/* 关键字 */
/* Literal */
/* Keyword */
.codehilite .l {
color: #1C01CE
}
/* 字面量 */
/* Name */
/* Literal */
.codehilite .n {
color: #000000
}
/* 名称 */
/* Operator */
/* Name */
.codehilite .o {
color: #000000
}
/* 操作符 */
/* Comment.Hashbang */
/* Operator */
.codehilite .ch {
color: #177500
}
/* Hashbang注释 */
/* Comment.Multiline */
/* Comment.Hashbang */
.codehilite .cm {
color: #177500
}
/* 多行注释 */
/* Comment.Preproc */
/* Comment.Multiline */
.codehilite .cp {
color: #633820
}
/* 预处理注释 */
/* Comment.PreprocFile */
/* Comment.Preproc */
.codehilite .cpf {
color: #177500
}
/* 文件预处理注释 */
/* Comment.Single */
/* Comment.PreprocFile */
.codehilite .c1 {
color: #177500
}
/* 单行注释 */
/* Comment.Special */
/* Comment.Single */
.codehilite .cs {
color: #177500
}
/* 特殊注释 */
/* Keyword.Constant */
/* Comment.Special */
.codehilite .kc {
color: #A90D91
}
/* 常量关键字 */
/* Keyword.Declaration */
/* Keyword.Constant */
.codehilite .kd {
color: #A90D91
}
/* 声明关键字 */
/* Keyword.Namespace */
/* Keyword.Declaration */
.codehilite .kn {
color: #A90D91
}
/* 命名空间关键字 */
/* Keyword.Pseudo */
/* Keyword.Namespace */
.codehilite .kp {
color: #A90D91
}
/* 伪关键字 */
/* Keyword.Reserved */
/* Keyword.Pseudo */
.codehilite .kr {
color: #A90D91
}
/* 保留关键字 */
/* Keyword.Type */
/* Keyword.Reserved */
.codehilite .kt {
color: #A90D91
}
/* 类型关键字 */
/* Literal.Date */
/* Keyword.Type */
.codehilite .ld {
color: #1C01CE
}
/* 日期字面量 */
/* Literal.Number */
/* Literal.Date */
.codehilite .m {
color: #1C01CE
}
/* 数字字面量 */
/* Literal.String */
/* Literal.Number */
.codehilite .s {
color: #C41A16
}
/* 字符串字面量 */
/* Name.Attribute */
/* Literal.String */
.codehilite .na {
color: #836C28
}
/* 属性名称 */
/* Name.Builtin */
/* Name.Attribute */
.codehilite .nb {
color: #A90D91
}
/* 内置名称 */
/* Name.Class */
/* Name.Builtin */
.codehilite .nc {
color: #3F6E75
}
/* 类名 */
/* Name.Constant */
/* Name.Class */
.codehilite .no {
color: #000000
}
/* 常量名称 */
/* Name.Decorator */
/* Name.Constant */
.codehilite .nd {
color: #000000
}
/* 装饰器名称 */
/* Name.Entity */
/* Name.Decorator */
.codehilite .ni {
color: #000000
}
/* 实体名称 */
/* Name.Exception */
/* Name.Entity */
.codehilite .ne {
color: #000000
}
/* 异常名称 */
/* Name.Function */
/* Name.Exception */
.codehilite .nf {
color: #000000
}
/* 函数名称 */
/* Name.Label */
/* Name.Function */
.codehilite .nl {
color: #000000
}
/* 标签名称 */
/* Name.Namespace */
/* Name.Label */
.codehilite .nn {
color: #000000
}
/* 命名空间名称 */
/* Name.Other */
/* Name.Namespace */
.codehilite .nx {
color: #000000
}
/* 其他名称 */
/* Name.Property */
/* Name.Other */
.codehilite .py {
color: #000000
}
/* 属性名称 */
/* Name.Tag */
/* Name.Property */
.codehilite .nt {
color: #000000
}
/* 标签名称 */
/* Name.Variable */
/* Name.Tag */
.codehilite .nv {
color: #000000
}
/* 变量名称 */
/* Operator.Word */
/* Name.Variable */
.codehilite .ow {
color: #000000
}
/* 操作符单词 */
/* Literal.Number.Bin */
/* Operator.Word */
.codehilite .mb {
color: #1C01CE
}
/* 二进制数字 */
/* Literal.Number.Float */
/* Literal.Number.Bin */
.codehilite .mf {
color: #1C01CE
}
/* 浮点数 */
/* Literal.Number.Hex */
/* Literal.Number.Float */
.codehilite .mh {
color: #1C01CE
}
/* 十六进制数 */
/* Literal.Number.Integer */
/* Literal.Number.Hex */
.codehilite .mi {
color: #1C01CE
}
/* 整数 */
/* Literal.Number.Oct */
/* Literal.Number.Integer */
.codehilite .mo {
color: #1C01CE
}
/* 八进制数 */
/* Literal.String.Backtick */
/* Literal.Number.Oct */
.codehilite .sb {
color: #C41A16
}
/* 反引号字符串 */
/* Literal.String.Char */
/* Literal.String.Backtick */
.codehilite .sc {
color: #2300CE
}
/* 字符 */
/* Literal.String.Doc */
/* Literal.String.Char */
.codehilite .sd {
color: #C41A16
}
/* 文档字符串 */
/* Literal.String.Double */
/* Literal.String.Doc */
.codehilite .s2 {
color: #C41A16
}
/* 双引号字符串 */
/* Literal.String.Escape */
/* Literal.String.Double */
.codehilite .se {
color: #C41A16
}
/* 转义字符串 */
/* Literal.String.Heredoc */
/* Literal.String.Escape */
.codehilite .sh {
color: #C41A16
}
/* Heredoc字符串 */
/* Literal.String.Interpol */
/* Literal.String.Heredoc */
.codehilite .si {
color: #C41A16
}
/* 插值字符串 */
/* Literal.String.Other */
/* Literal.String.Interpol */
.codehilite .sx {
color: #C41A16
}
/* 其他字符串 */
/* Literal.String.Regex */
/* Literal.String.Other */
.codehilite .sr {
color: #C41A16
}
/* 正则表达式 */
/* Literal.String.Single */
/* Literal.String.Regex */
.codehilite .s1 {
color: #C41A16
}
/* 单引号字符串 */
/* Literal.String.Symbol */
/* Literal.String.Single */
.codehilite .ss {
color: #C41A16
}
/* 符号 */
/* Name.Builtin.Pseudo */
/* Literal.String.Symbol */
.codehilite .bp {
color: #5B269A
}
/* 伪内置名称 */
/* Name.Variable.Class */
/* Name.Builtin.Pseudo */
.codehilite .vc {
color: #000000
}
/* 类变量 */
/* Name.Variable.Global */
/* Name.Variable.Class */
.codehilite .vg {
color: #000000
}
/* 全局变量 */
/* Name.Variable.Instance */
/* Name.Variable.Global */
.codehilite .vi {
color: #000000
}
/* 实例变量 */
/* Literal.Number.Integer.Long */
/* Name.Variable.Instance */
.codehilite .il {
color: #1C01CE
}
/* 长整数 */
/* Literal.Number.Integer.Long */

@ -51,7 +51,75 @@ def datetimeformat(data):
@register.filter()
@stringfilter
def custom_markdown(content):
return mark_safe(CommonMarkdown.get_markdown(content))
"""
通用markdown过滤器应用文章内容插件
主要用于文章内容处理
"""
html_content = CommonMarkdown.get_markdown(content)
# 然后应用插件过滤器优化HTML
from djangoblog.plugin_manage import hooks
from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME
optimized_html = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, html_content)
return mark_safe(optimized_html)
@register.filter()
@stringfilter
def sidebar_markdown(content):
html_content = CommonMarkdown.get_markdown(content)
return mark_safe(html_content)
@register.simple_tag(takes_context=True)
def render_article_content(context, article, is_summary=False):
"""
渲染文章内容包含完整的上下文信息供插件使用
Args:
context: 模板上下文
article: 文章对象
is_summary: 是否为摘要模式首页使用
"""
if not article or not hasattr(article, 'body'):
return ''
# 先转换Markdown为HTML
html_content = CommonMarkdown.get_markdown(article.body)
# 如果是摘要模式,先截断内容再应用插件
if is_summary:
# 截断HTML内容到合适的长度约300字符
from django.utils.html import strip_tags
from django.template.defaultfilters import truncatechars
# 先去除HTML标签截断纯文本然后重新转换为HTML
plain_text = strip_tags(html_content)
truncated_text = truncatechars(plain_text, 300)
# 重新转换截断后的文本为HTML简化版避免复杂的插件处理
html_content = CommonMarkdown.get_markdown(truncated_text)
# 然后应用插件过滤器,传递完整的上下文
from djangoblog.plugin_manage import hooks
from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME
# 获取request对象
request = context.get('request')
# 应用所有文章内容相关的插件
# 注意:摘要模式下某些插件(如版权声明)可能不适用
optimized_html = hooks.apply_filters(
ARTICLE_CONTENT_HOOK_NAME,
html_content,
article=article,
request=request,
context=context,
is_summary=is_summary # 传递摘要标志,插件可以据此调整行为
)
return mark_safe(optimized_html)
@register.simple_tag
@ -205,7 +273,7 @@ def load_article_metas(article, user):
@register.inclusion_tag('blog/tags/article_pagination.html')
def load_pagination_info(page_obj, page_type, tag_name):
def load_pagination_info(page_obj, page_type, tag_name, sort=None):
previous_url = ''
next_url = ''
if page_type == '':
@ -266,6 +334,12 @@ def load_pagination_info(page_obj, page_type, tag_name):
'page': previous_number,
'category_name': category.slug})
if sort:
if next_url:
next_url = f"{next_url}?sort={sort}"
if previous_url:
previous_url = f"{previous_url}?sort={sort}"
return {
'previous_url': previous_url,
'next_url': next_url,
@ -273,8 +347,8 @@ def load_pagination_info(page_obj, page_type, tag_name):
}
@register.inclusion_tag('blog/tags/article_info.html')
def load_article_detail(article, isindex, user):
@register.inclusion_tag('blog/tags/article_info.html', takes_context=True)
def load_article_detail(context, article, isindex, user):
"""
加载文章详情
:param article:
@ -284,46 +358,71 @@ def load_article_detail(article, isindex, user):
from djangoblog.utils import get_blog_setting
blogsetting = get_blog_setting()
request = context.get('request')
liked_ids = request.session.get('liked_articles', []) if request else []
favorited_ids = request.session.get('favorited_articles', []) if request else []
has_liked = False
has_favorited = False
if request and hasattr(request, 'user') and request.user.is_authenticated:
has_liked = article.like_users.filter(pk=request.user.pk).exists()
has_favorited = article.favorite_users.filter(pk=request.user.pk).exists()
else:
has_liked = article.id in liked_ids
has_favorited = article.id in favorited_ids
return {
'article': article,
'isindex': isindex,
'user': user,
'open_site_comment': blogsetting.open_site_comment,
'has_liked': has_liked,
'has_favorited': has_favorited,
}
# return only the URL of the gravatar
# TEMPLATE USE: {{ email|gravatar_url:150 }}
# 返回用户头像URL
# 模板使用方法: {{ email|gravatar_url:150 }}
@register.filter
def gravatar_url(email, size=40):
"""获得gravatar头像"""
cachekey = 'gravatat/' + email
"""获得用户头像 - 优先使用OAuth头像否则使用默认头像"""
cachekey = 'avatar/' + email
url = cache.get(cachekey)
if url:
return url
else:
usermodels = OAuthUser.objects.filter(email=email)
if usermodels:
o = list(filter(lambda x: x.picture is not None, usermodels))
if o:
return o[0].picture
email = email.encode('utf-8')
default = static('blog/img/avatar.png')
url = "https://www.gravatar.com/avatar/%s?%s" % (hashlib.md5(
email.lower()).hexdigest(), urllib.parse.urlencode({'d': default, 's': str(size)}))
cache.set(cachekey, url, 60 * 60 * 10)
logger.info('set gravatar cache.key:{key}'.format(key=cachekey))
return url
# 检查OAuth用户是否有自定义头像
usermodels = OAuthUser.objects.filter(email=email)
if usermodels:
# 过滤出有头像的用户
users_with_picture = list(filter(lambda x: x.picture is not None, usermodels))
if users_with_picture:
# 获取默认头像路径用于比较
default_avatar_path = static('blog/img/avatar.png')
# 优先选择非默认头像的用户,否则选择第一个
non_default_users = [u for u in users_with_picture if u.picture != default_avatar_path and not u.picture.endswith('/avatar.png')]
selected_user = non_default_users[0] if non_default_users else users_with_picture[0]
url = selected_user.picture
cache.set(cachekey, url, 60 * 60 * 24) # 缓存24小时
avatar_type = 'non-default' if non_default_users else 'default'
logger.info('Using {} OAuth avatar for {} from {}'.format(avatar_type, email, selected_user.type))
return url
# 使用默认头像
url = static('blog/img/avatar.png')
cache.set(cachekey, url, 60 * 60 * 24) # 缓存24小时
logger.info('Using default avatar for {}'.format(email))
return url
@register.filter
def gravatar(email, size=40):
"""获得gravatar头像"""
"""获得用户头像HTML标签"""
url = gravatar_url(email, size)
return mark_safe(
'<img src="%s" height="%d" width="%d">' %
'<img src="%s" height="%d" width="%d" class="avatar" alt="用户头像">' %
(url, size, size))
@ -342,3 +441,134 @@ def query(qs, **kwargs):
def addstr(arg1, arg2):
"""concatenate arg1 & arg2"""
return str(arg1) + str(arg2)
# === 插件系统模板标签 ===
@register.simple_tag(takes_context=True)
def render_plugin_widgets(context, position, **kwargs):
"""
渲染指定位置的所有插件组件
Args:
context: 模板上下文
position: 位置标识
**kwargs: 传递给插件的额外参数
Returns:
按优先级排序的所有插件HTML内容
"""
from djangoblog.plugin_manage.loader import get_loaded_plugins
widgets = []
for plugin in get_loaded_plugins():
try:
widget_data = plugin.render_position_widget(
position=position,
context=context,
**kwargs
)
if widget_data:
widgets.append(widget_data)
except Exception as e:
logger.error(f"Error rendering widget from plugin {plugin.PLUGIN_NAME}: {e}")
# 按优先级排序(数字越小优先级越高)
widgets.sort(key=lambda x: x['priority'])
# 合并HTML内容
html_parts = [widget['html'] for widget in widgets]
return mark_safe(''.join(html_parts))
@register.simple_tag(takes_context=True)
def plugin_head_resources(context):
"""渲染所有插件的head资源仅自定义HTMLCSS已集成到压缩系统"""
from djangoblog.plugin_manage.loader import get_loaded_plugins
resources = []
for plugin in get_loaded_plugins():
try:
# 只处理自定义head HTMLCSS文件已通过压缩系统处理
head_html = plugin.get_head_html(context)
if head_html:
resources.append(head_html)
except Exception as e:
logger.error(f"Error loading head resources from plugin {plugin.PLUGIN_NAME}: {e}")
return mark_safe('\n'.join(resources))
@register.simple_tag(takes_context=True)
def plugin_body_resources(context):
"""渲染所有插件的body资源仅自定义HTMLJS已集成到压缩系统"""
from djangoblog.plugin_manage.loader import get_loaded_plugins
resources = []
for plugin in get_loaded_plugins():
try:
# 只处理自定义body HTMLJS文件已通过压缩系统处理
body_html = plugin.get_body_html(context)
if body_html:
resources.append(body_html)
except Exception as e:
logger.error(f"Error loading body resources from plugin {plugin.PLUGIN_NAME}: {e}")
return mark_safe('\n'.join(resources))
@register.inclusion_tag('plugins/css_includes.html')
def plugin_compressed_css():
"""插件CSS压缩包含模板"""
from djangoblog.plugin_manage.loader import get_loaded_plugins
css_files = []
for plugin in get_loaded_plugins():
for css_file in plugin.get_css_files():
css_url = plugin.get_static_url(css_file)
css_files.append(css_url)
return {'css_files': css_files}
@register.inclusion_tag('plugins/js_includes.html')
def plugin_compressed_js():
"""插件JS压缩包含模板"""
from djangoblog.plugin_manage.loader import get_loaded_plugins
js_files = []
for plugin in get_loaded_plugins():
for js_file in plugin.get_js_files():
js_url = plugin.get_static_url(js_file)
js_files.append(js_url)
return {'js_files': js_files}
@register.simple_tag(takes_context=True)
def plugin_widget(context, plugin_name, widget_type='default', **kwargs):
"""
渲染指定插件的组件
使用方式
{% plugin_widget 'article_recommendation' 'bottom' article=article count=5 %}
"""
from djangoblog.plugin_manage.loader import get_plugin_by_slug
plugin = get_plugin_by_slug(plugin_name)
if plugin and hasattr(plugin, 'render_template'):
try:
widget_context = {**context.flatten(), **kwargs}
template_name = f"{widget_type}.html"
return mark_safe(plugin.render_template(template_name, widget_context))
except Exception as e:
logger.error(f"Error rendering plugin widget {plugin_name}.{widget_type}: {e}")
return ""

@ -230,3 +230,57 @@ class ArticleTest(TestCase):
call_command("clear_cache")
call_command("sync_user_avatar")
call_command("build_search_words")
def test_like_and_favorite(self):
user = BlogUser.objects.get_or_create(
email="user@test.com",
username="user1")[0]
user.set_password("pwd12345")
user.save()
category = Category()
category.name = "likecat"
category.save()
article = Article()
article.title = "likefavtitle"
article.body = "body"
article.author = user
article.category = category
article.type = 'a'
article.status = 'p'
article.save()
like_url = reverse('blog:like_article', kwargs={'article_id': article.id})
fav_url = reverse('blog:favorite_article', kwargs={'article_id': article.id})
rsp = self.client.post(like_url)
self.assertEqual(rsp.status_code, 200)
article.refresh_from_db()
self.assertEqual(article.like_count, 1)
rsp = self.client.post(like_url)
article.refresh_from_db()
self.assertEqual(article.like_count, 1)
rsp = self.client.post(fav_url)
self.assertEqual(rsp.status_code, 200)
article.refresh_from_db()
self.assertEqual(article.favorite_count, 1)
rsp = self.client.post(fav_url)
article.refresh_from_db()
self.assertEqual(article.favorite_count, 1)
self.client.login(username='user1', password='pwd12345')
rsp = self.client.post(like_url)
article.refresh_from_db()
self.assertEqual(article.like_count, 2)
rsp = self.client.post(like_url)
article.refresh_from_db()
self.assertEqual(article.like_count, 2)
rsp = self.client.post(fav_url)
article.refresh_from_db()
self.assertEqual(article.favorite_count, 2)
rsp = self.client.post(fav_url)
article.refresh_from_db()
self.assertEqual(article.favorite_count, 2)

@ -41,6 +41,14 @@ urlpatterns = [
r'tag/<slug:tag_name>/<int:page>.html',
views.TagDetailView.as_view(),
name='tag_detail_page'),
path(
r'article/<int:article_id>/like/',
views.like_article,
name='like_article'),
path(
r'article/<int:article_id>/favorite/',
views.favorite_article,
name='favorite_article'),
path(
'archives.html',
cache_page(

@ -5,6 +5,7 @@ import uuid
from django.conf import settings
from django.core.paginator import Paginator
from django.http import HttpResponse, HttpResponseForbidden
from django.http import JsonResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.templatetags.static import static
@ -59,6 +60,20 @@ class ArticleListView(ListView):
"""
raise NotImplementedError()
def get_sort(self):
sort = self.request.GET.get('sort', 'latest')
return sort if sort in ('latest', 'hot') else 'latest'
def order_queryset(self, qs):
sort = self.get_sort()
if sort == 'hot':
from django.utils import timezone
from datetime import timedelta
week_ago = timezone.now() - timedelta(days=7)
qs = qs.filter(pub_time__gte=week_ago)
return qs.order_by('-article_order', '-views', '-pub_time')
return qs.order_by('-article_order', '-pub_time')
def get_queryset_from_cache(self, cache_key):
'''
缓存页面数据
@ -86,6 +101,7 @@ class ArticleListView(ListView):
def get_context_data(self, **kwargs):
kwargs['linktype'] = self.link_type
kwargs['sort'] = self.get_sort()
return super(ArticleListView, self).get_context_data(**kwargs)
@ -97,11 +113,11 @@ class IndexView(ArticleListView):
link_type = LinkShowType.I
def get_queryset_data(self):
article_list = Article.objects.filter(type='a', status='p')
return article_list
qs = Article.objects.filter(type='a', status='p')
return self.order_queryset(qs)
def get_queryset_cache_key(self):
cache_key = 'index_{page}'.format(page=self.page_number)
cache_key = 'index_{page}_sort_{sort}'.format(page=self.page_number, sort=self.get_sort())
return cache_key
@ -152,12 +168,21 @@ class ArticleDetailView(DetailView):
context = super(ArticleDetailView, self).get_context_data(**kwargs)
article = self.object
# 触发文章详情加载钩子,让插件可以添加额外的上下文数据
from djangoblog.plugin_manage.hook_constants import ARTICLE_DETAIL_LOAD
hooks.run_action(ARTICLE_DETAIL_LOAD, article=article, context=context, request=self.request)
# Action Hook, 通知插件"文章详情已获取"
hooks.run_action('after_article_body_get', article=article, request=self.request)
# # Filter Hook, 允许插件修改文章正文
article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article,
request=self.request)
liked_ids = self.request.session.get('liked_articles', [])
favorited_ids = self.request.session.get('favorited_articles', [])
context['has_liked'] = (
self.request.user.is_authenticated and article.like_users.filter(pk=self.request.user.pk).exists()
) or (article.id in liked_ids)
context['has_favorited'] = (
self.request.user.is_authenticated and article.favorite_users.filter(pk=self.request.user.pk).exists()
) or (article.id in favorited_ids)
return context
@ -175,17 +200,17 @@ class CategoryDetailView(ArticleListView):
self.categoryname = categoryname
categorynames = list(
map(lambda c: c.name, category.get_sub_categorys()))
article_list = Article.objects.filter(
qs = Article.objects.filter(
category__name__in=categorynames, status='p')
return article_list
return self.order_queryset(qs)
def get_queryset_cache_key(self):
slug = self.kwargs['category_name']
category = get_object_or_404(Category, slug=slug)
categoryname = category.name
self.categoryname = categoryname
cache_key = 'category_list_{categoryname}_{page}'.format(
categoryname=categoryname, page=self.page_number)
cache_key = 'category_list_{categoryname}_{page}_sort_{sort}'.format(
categoryname=categoryname, page=self.page_number, sort=self.get_sort())
return cache_key
def get_context_data(self, **kwargs):
@ -209,15 +234,15 @@ class AuthorDetailView(ArticleListView):
def get_queryset_cache_key(self):
from uuslug import slugify
author_name = slugify(self.kwargs['author_name'])
cache_key = 'author_{author_name}_{page}'.format(
author_name=author_name, page=self.page_number)
cache_key = 'author_{author_name}_{page}_sort_{sort}'.format(
author_name=author_name, page=self.page_number, sort=self.get_sort())
return cache_key
def get_queryset_data(self):
author_name = self.kwargs['author_name']
article_list = Article.objects.filter(
qs = Article.objects.filter(
author__username=author_name, type='a', status='p')
return article_list
return self.order_queryset(qs)
def get_context_data(self, **kwargs):
author_name = self.kwargs['author_name']
@ -237,17 +262,17 @@ class TagDetailView(ArticleListView):
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
self.name = tag_name
article_list = Article.objects.filter(
qs = Article.objects.filter(
tags__name=tag_name, type='a', status='p')
return article_list
return self.order_queryset(qs)
def get_queryset_cache_key(self):
slug = self.kwargs['tag_name']
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
self.name = tag_name
cache_key = 'tag_{tag_name}_{page}'.format(
tag_name=tag_name, page=self.page_number)
cache_key = 'tag_{tag_name}_{page}_sort_{sort}'.format(
tag_name=tag_name, page=self.page_number, sort=self.get_sort())
return cache_key
def get_context_data(self, **kwargs):
@ -354,6 +379,50 @@ def page_not_found_view(
status=404)
@csrf_exempt
def like_article(request, article_id):
if request.method != 'POST':
return HttpResponseBadRequest('only post')
article = get_object_or_404(Article, pk=article_id)
if request.user.is_authenticated:
if article.like_users.filter(pk=request.user.pk).exists():
return JsonResponse({'ok': False, 'message': '已点赞', 'like_count': article.like_count})
article.like_users.add(request.user)
article.like_count += 1
article.save(update_fields=['like_count'])
return JsonResponse({'ok': True, 'message': '点赞成功', 'like_count': article.like_count})
liked = request.session.get('liked_articles', [])
if article.id in liked:
return JsonResponse({'ok': False, 'message': '已点赞', 'like_count': article.like_count})
liked.append(article.id)
request.session['liked_articles'] = liked
article.like_count += 1
article.save(update_fields=['like_count'])
return JsonResponse({'ok': True, 'message': '点赞成功', 'like_count': article.like_count})
@csrf_exempt
def favorite_article(request, article_id):
if request.method != 'POST':
return HttpResponseBadRequest('only post')
article = get_object_or_404(Article, pk=article_id)
if request.user.is_authenticated:
if article.favorite_users.filter(pk=request.user.pk).exists():
return JsonResponse({'ok': False, 'message': '已收藏', 'favorite_count': article.favorite_count})
article.favorite_users.add(request.user)
article.favorite_count += 1
article.save(update_fields=['favorite_count'])
return JsonResponse({'ok': True, 'message': '收藏成功', 'favorite_count': article.favorite_count})
favorited = request.session.get('favorited_articles', [])
if article.id in favorited:
return JsonResponse({'ok': False, 'message': '已收藏', 'favorite_count': article.favorite_count})
favorited.append(article.id)
request.session['favorited_articles'] = favorited
article.favorite_count += 1
article.save(update_fields=['favorite_count'])
return JsonResponse({'ok': True, 'message': '收藏成功', 'favorite_count': article.favorite_count})
def server_error_view(request, template_name='blog/error_page.html'):
return render(request,
template_name,

@ -0,0 +1,87 @@
codecov:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "70...100"
status:
project:
default:
target: auto
threshold: 1%
informational: true
patch:
default:
target: auto
threshold: 1%
informational: true
parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no
comment:
layout: "reach,diff,flags,tree"
behavior: default
require_changes: no
ignore:
# Django 相关
- "*/migrations/*"
- "manage.py"
- "*/settings.py"
- "*/wsgi.py"
- "*/asgi.py"
# 测试相关
- "*/tests/*"
- "*/test_*.py"
- "*/*test*.py"
# 静态文件和模板
- "*/static/*"
- "*/templates/*"
- "*/collectedstatic/*"
# 国际化文件
- "*/locale/*"
- "**/*.po"
- "**/*.mo"
# 文档和部署
- "*/docs/*"
- "*/deploy/*"
- "README*.md"
- "LICENSE"
- "Dockerfile"
- "docker-compose*.yml"
- "*.yaml"
- "*.yml"
# 开发环境
- "*/venv/*"
- "*/__pycache__/*"
- "*.pyc"
- ".coverage"
- "coverage.xml"
# 日志文件
- "*/logs/*"
- "*.log"
# 特定文件
- "*/whoosh_cn_backend.py" # 搜索后端
- "*/elasticsearch_backend.py" # 搜索后端
- "*/MemcacheStorage.py" # 缓存存储
- "*/robot.py" # 机器人相关
# 配置文件
- "codecov.yml"
- ".coveragerc"
- "requirements*.txt"

File diff suppressed because one or more lines are too long

@ -1,688 +0,0 @@
/*
*
*/
/* Google 图标样式 */
.icon-sn-google {
background-position: 0 -28px;
}
.icon-sn-bg-google {
background-color: #4285f4;
background-position: 0 0;
}
.fa-sn-google {
color: #4285f4;
}
/* GitHub 图标样式 */
.icon-sn-github {
background-position: -28px -28px;
}
.icon-sn-bg-github {
background-color: #333;
background-position: -28px 0;
}
.fa-sn-github {
color: #333;
}
/* 微博图标样式 */
.icon-sn-weibo {
background-position: -56px -28px;
}
.icon-sn-bg-weibo {
background-color: #e90d24;
background-position: -56px 0;
}
.fa-sn-weibo {
color: #e90d24;
}
/* QQ 图标样式 */
.icon-sn-qq {
background-position: -84px -28px;
}
.icon-sn-bg-qq {
background-color: #0098e6;
background-position: -84px 0;
}
.fa-sn-qq {
color: #0098e6;
}
/* Twitter 图标样式 */
.icon-sn-twitter {
background-position: -112px -28px;
}
.icon-sn-bg-twitter {
background-color: #50abf1;
background-position: -112px 0;
}
.fa-sn-twitter {
color: #50abf1;
}
/* Facebook 图标样式 */
.icon-sn-facebook {
background-position: -140px -28px;
}
.icon-sn-bg-facebook {
background-color: #4862a3;
background-position: -140px 0;
}
.fa-sn-facebook {
color: #4862a3;
}
/* 人人网图标样式 */
.icon-sn-renren {
background-position: -168px -28px;
}
.icon-sn-bg-renren {
background-color: #197bc8;
background-position: -168px 0;
}
.fa-sn-renren {
color: #197bc8;
}
/* 腾讯微博图标样式 */
.icon-sn-tqq {
background-position: -196px -28px;
}
.icon-sn-bg-tqq {
background-color: #1f9ed2;
background-position: -196px 0;
}
.fa-sn-tqq {
color: #1f9ed2;
}
/* 豆瓣图标样式 */
.icon-sn-douban {
background-position: -224px -28px;
}
.icon-sn-bg-douban {
background-color: #279738;
background-position: -224px 0;
}
.fa-sn-douban {
color: #279738;
}
/* 微信图标样式 */
.icon-sn-weixin {
background-position: -252px -28px;
}
.icon-sn-bg-weixin {
background-color: #00b500;
background-position: -252px 0;
}
.fa-sn-weixin {
color: #00b500;
}
/* 虚线图标样式 */
.icon-sn-dotted {
background-position: -280px -28px;
}
.icon-sn-bg-dotted {
background-color: #eee;
background-position: -280px 0;
}
.fa-sn-dotted {
color: #eee;
}
/* 站点图标样式 */
.icon-sn-site {
background-position: -308px -28px;
}
.icon-sn-bg-site {
background-color: #00b500;
background-position: -308px 0;
}
.fa-sn-site {
color: #00b500;
}
/* LinkedIn 图标样式 */
.icon-sn-linkedin {
background-position: -336px -28px;
}
.icon-sn-bg-linkedin {
background-color: #0077b9;
background-position: -336px 0;
}
.fa-sn-linkedin {
color: #0077b9;
}
/* 通用图标样式 */
[class*=icon-sn-] {
display: inline-block;
background-image: url('/static/blog/img/icon-sn.svg?56272f05e520');
background-repeat: no-repeat;
width: 28px;
height: 28px;
vertical-align: middle;
background-size: auto 56px;
}
[class*=icon-sn-]:hover {
opacity: .8;
filter: alpha(opacity=80);
}
/*
*
*/
/* Google 按钮样式 */
.btn-sn-google {
background: #4285f4;
}
.btn-sn-google:active,
.btn-sn-google:focus,
.btn-sn-google:hover {
background: #2a75f3;
}
/* GitHub 按钮样式 */
.btn-sn-github {
background: #333;
}
.btn-sn-github:active,
.btn-sn-github:focus,
.btn-sn-github:hover {
background: #262626;
}
/* 微博按钮样式 */
.btn-sn-weibo {
background: #e90d24;
}
.btn-sn-weibo:active,
.btn-sn-weibo:focus,
.btn-sn-weibo:hover {
background: #d10c20;
}
/* QQ 按钮样式 */
.btn-sn-qq {
background: #0098e6;
}
.btn-sn-qq:active,
.btn-sn-qq:focus,
.btn-sn-qq:hover {
background: #0087cd;
}
/* Twitter 按钮样式 */
.btn-sn-twitter {
background: #50abf1;
}
.btn-sn-twitter:active,
.btn-sn-twitter:focus,
.btn-sn-twitter:hover {
background: #38a0ef;
}
/* Facebook 按钮样式 */
.btn-sn-facebook {
background: #4862a3;
}
.btn-sn-facebook:active,
.btn-sn-facebook:focus,
.btn-sn-facebook:hover {
background: #405791;
}
/* 人人网按钮样式 */
.btn-sn-renren {
background: #197bc8;
}
.btn-sn-renren:active,
.btn-sn-renren:focus,
.btn-sn-renren:hover {
background: #166db1;
}
/* 腾讯微博按钮样式 */
.btn-sn-tqq {
background: #1f9ed2;
}
.btn-sn-tqq:active,
.btn-sn-tqq:focus,
.btn-sn-tqq:hover {
background: #1c8dbc;
}
/* 豆瓣按钮样式 */
.btn-sn-douban {
background: #279738;
}
.btn-sn-douban:active,
.btn-sn-douban:focus,
.btn-sn-douban:hover {
background: #228330;
}
/* 微信按钮样式 */
.btn-sn-weixin {
background: #00b500;
}
.btn-sn-weixin:active,
.btn-sn-weixin:focus,
.btn-sn-weixin:hover {
background: #009c00;
}
/* 虚线按钮样式 */
.btn-sn-dotted {
background: #eee;
}
.btn-sn-dotted:active,
.btn-sn-dotted:focus,
.btn-sn-dotted:hover {
background: #e1e1e1;
}
/* 站点按钮样式 */
.btn-sn-site {
background: #00b500;
}
.btn-sn-site:active,
.btn-sn-site:focus,
.btn-sn-site:hover {
background: #009c00;
}
/* LinkedIn 按钮样式 */
.btn-sn-linkedin {
background: #0077b9;
}
.btn-sn-linkedin:active,
.btn-sn-linkedin:focus,
.btn-sn-linkedin:hover {
background: #0067a0;
}
/* 通用按钮样式 */
[class*=btn-sn-],
[class*=btn-sn-]:active,
[class*=btn-sn-]:focus,
[class*=btn-sn-]:hover {
border: none;
color: #fff;
}
.btn-sn-more {
padding: 0;
}
.btn-sn-more,
.btn-sn-more:active,
.btn-sn-more:hover {
box-shadow: none;
}
[class*=btn-sn-] [class*=icon-sn-] {
background-color: transparent;
}
/*
*
*/
.codehilite .hll {
background-color: #ffffcc;
}
.codehilite {
background: #ffffff;
}
.codehilite .c {
color: #177500;
}
.codehilite .err {
color: #000000;
}
.codehilite .k {
color: #A90D91;
}
.codehilite .l {
color: #1C01CE;
}
.codehilite .n {
color: #000000;
}
.codehilite .o {
color: #000000;
}
.codehilite .ch {
color: #177500;
}
.codehilite .cm {
color: #177500;
}
.codehilite .cp {
color: #633820;
}
.codehilite .cpf {
color: #177500;
}
.codehilite .c1 {
color: #177500;
}
.codehilite .cs {
color: #177500;
}
.codehilite .kc {
color: #A90D91;
}
.codehilite .kd {
color: #A90D91;
}
.codehilite .kn {
color: #A90D91;
}
.codehilite .kp {
color: #A90D91;
}
.codehilite .kr {
color: #A90D91;
}
.codehilite .kt {
color: #A90D91;
}
.codehilite .ld {
color: #1C01CE;
}
.codehilite .m {
color: #1C01CE;
}
.codehilite .s {
color: #C41A16;
}
.codehilite .na {
color: #836C28;
}
.codehilite .nb {
color: #A90D91;
}
.codehilite .nc {
color: #3F6E75;
}
.codehilite .no {
color: #000000;
}
.codehilite .nd {
color: #000000;
}
.codehilite .ni {
color: #000000;
}
.codehilite .ne {
color: #000000;
}
.codehilite .nf {
color: #000000;
}
.codehilite .nl {
color: #000000;
}
.codehilite .nn {
color: #000000;
}
.codehilite .nx {
color: #000000;
}
.codehilite .py {
color: #000000;
}
.codehilite .nt {
color: #000000;
}
.codehilite .nv {
color: #000000;
}
.codehilite .ow {
color: #000000;
}
.codehilite .mb {
color: #1C01CE;
}
.codehilite .mf {
color: #1C01CE;
}
.codehilite .mh {
color: #1C01CE;
}
.codehilite .mi {
color: #1C01CE;
}
.codehilite .mo {
color: #1C01CE;
}
.codehilite .sb {
color: #C41A16;
}
.codehilite .sc {
color: #2300CE;
}
.codehilite .sd {
color: #C41A16;
}
.codehilite .s2 {
color: #C41A16;
}
.codehilite .se {
color: #C41A16;
}
.codehilite .sh {
color: #C41A16;
}
.codehilite .si {
color: #C41A16;
}
.codehilite .sx {
color: #C41A16;
}
.codehilite .sr {
color: #C41A16;
}
.codehilite .s1 {
color: #C41A16;
}
.codehilite .ss {
color: #C41A16;
}
.codehilite .bp {
color: #5B269A;
}
.codehilite .vc {
color: #000000;
}
.codehilite .vg {
color: #000000;
}
.codehilite .vi {
color: #000000;
}
.codehilite .il {
color: #1C01CE;
}
/*
* (NProgress)
*/
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: red;
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: red;
border-left-color: red;
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes nprogress-spinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,26 +0,0 @@
/*!
* IE10 viewport hack for Surface/desktop Windows 8 bug
* Copyright 2014-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/(function(){'use strict';if(navigator.userAgent.match(/IEMobile\/10\.0/)){var msViewportStyle=document.createElement('style')
msViewportStyle.appendChild(document.createTextNode('@-ms-viewport{width:auto!important}'))
document.querySelector('head').appendChild(msViewportStyle)}})();;/*!
* Copyright 2014-2015 Twitter, Inc.
*
* Licensed under the Creative Commons Attribution 3.0 Unported License. For
* details, see https://creativecommons.org/licenses/by/3.0/.
*/(function(){'use strict';function emulatedIEMajorVersion(){var groups=/MSIE ([0-9.]+)/.exec(window.navigator.userAgent)
if(groups===null){return null}
var ieVersionNum=parseInt(groups[1],10)
var ieMajorVersion=Math.floor(ieVersionNum)
return ieMajorVersion}
function actualNonEmulatedIEMajorVersion(){var jscriptVersion=new Function('/*@cc_on return @_jscript_version; @*/')()
if(jscriptVersion===undefined){return 11}
if(jscriptVersion<9){return 8}
return jscriptVersion}
var ua=window.navigator.userAgent
if(ua.indexOf('Opera')>-1||ua.indexOf('Presto')>-1){return}
var emulated=emulatedIEMajorVersion()
if(emulated===null){return}
var nonEmulated=actualNonEmulatedIEMajorVersion()
if(emulated!==nonEmulated){window.alert('WARNING: You appear to be using IE'+nonEmulated+' in IE'+emulated+' emulation mode.\nIE emulation modes can behave significantly differently from ACTUAL older versions of IE.\nPLEASE DON\'T FILE BOOTSTRAP BUGS based on testing in IE emulation modes!')}})();;

@ -1,6 +0,0 @@
{
"e33c03e5cececc24214f8c70d7bfaf0079144308b8384c2b99378574a030b9a3": "<link rel=\"stylesheet\" href=\"/__compressor_url_placeholder__/CACHE/css/output.758e876fbda7.css\" type=\"text/css\" media=\"all\"><link rel=\"stylesheet\" href=\"/__compressor_url_placeholder__/CACHE/css/output.20f74afba408.css\" type=\"text/css\">",
"8efb09bae7df3f7f0c86c3573cd92622e620c23d356cee88d602cc06cd8ba0a5": "<link rel=\"stylesheet\" href=\"/__compressor_url_placeholder__/CACHE/css/output.5ee9ff3cb1a7.css\" type=\"text/css\">",
"602f536ee15494b2c004d9caae6d729f444aeae603707972c22cb7085ef69aa8": "<script src=\"/__compressor_url_placeholder__/CACHE/js/output.bc55ccd28723.js\"></script>",
"2e8e3574500075700cfa894c402675e08c87a3b71b31ed7d0002d493a526bcc8": "<script src=\"/__compressor_url_placeholder__/CACHE/js/output.de188198a436.js\"></script>"
}

@ -1,25 +0,0 @@
/* 按钮样式 */
/* Button styles */
.button {
/* 移除边框 */
/* Remove border */
border: none;
/* 设置内边距 */
/* Set padding */
padding: 4px 80px;
/* 设置文本对齐方式 */
/* Set text alignment */
text-align: center;
/* 移除文本装饰 */
/* Remove text decoration */
text-decoration: none;
/* 设置为行内块元素 */
/* Set as inline-block element */
display: inline-block;
/* 设置字体大小 */
/* Set font size */
font-size: 16px;
/* 设置外边距 */
/* Set margin */
margin: 4px 2px;
}

@ -1,101 +0,0 @@
// 设置倒计时初始值为60秒
// Set initial countdown value to 60 seconds
let wait = 60;
// 倒计时函数,用于在发送验证码按钮上显示倒计时
// Countdown function to display countdown on the send verification code button
function time(o) {
// 如果倒计时结束
// If countdown is over
if (wait == 0) {
// 移除按钮的禁用状态
// Remove button disabled state
o.removeAttribute("disabled");
// 恢复按钮文本
// Restore button text
o.value = "获取验证码";
// 重置倒计时
// Reset countdown
wait = 60
return false
} else {
// 设置按钮为禁用状态
// Set button to disabled state
o.setAttribute("disabled", true);
// 更新按钮文本显示倒计时
// Update button text to show countdown
o.value = "重新发送(" + wait + ")";
// 倒计时减1
// Decrease countdown by 1
wait--;
// 1秒后递归调用此函数
// Recursively call this function after 1 second
setTimeout(function () {
time(o)
},
1000)
}
}
// 获取验证码按钮点击事件处理
// Verification code button click event handler
document.getElementById("btn").onclick = function () {
// 获取邮箱输入框元素
// Get email input element
let id_email = $("#id_email")
// 获取CSRF令牌
// Get CSRF token
let token = $("*[name='csrfmiddlewaretoken']").val()
// 获取当前按钮元素
// Get current button element
let ts = this
// 获取错误信息显示元素
// Get error message display element
let myErr = $("#myErr")
// 发送AJAX请求
// Send AJAX request
$.ajax(
{
// 请求URL
// Request URL
url: "/forget_password_code/",
// 请求类型
// Request type
type: "POST",
// 请求数据
// Request data
data: {
"email": id_email.val(),
"csrfmiddlewaretoken": token
},
// 请求成功回调函数
// Success callback function
success: function (result) {
// 如果返回结果不是"ok"
// If the returned result is not "ok"
if (result != "ok") {
// 移除旧的错误信息
// Remove old error message
myErr.remove()
// 在邮箱输入框后添加新的错误信息
// Add new error message after email input
id_email.after("<ul className='errorlist' id='myErr'><li>" + result + "</li></ul>")
return
}
// 移除错误信息
// Remove error message
myErr.remove()
// 启动倒计时
// Start countdown
time(ts)
},
// 请求失败回调函数
// Error callback function
error: function (e) {
// 弹出发送失败提示
// Show send failure alert
alert("发送失败,请重试")
}
}
);
}

@ -1,332 +0,0 @@
/* 管理界面自动补全样式 */
/* Admin autocomplete styles */
/* 选择器宽度设置 */
select.admin-autocomplete {
width: 20em;
}
/* 自动补全容器最小高度设置 */
.select2-container--admin-autocomplete.select2-container {
min-height: 30px;
}
/* 单选和多选容器最小高度设置 */
.select2-container--admin-autocomplete .select2-selection--single,
.select2-container--admin-autocomplete .select2-selection--multiple {
min-height: 30px;
padding: 0;
}
/* 聚焦或打开状态下的选择器边框颜色 */
.select2-container--admin-autocomplete.select2-container--focus .select2-selection,
.select2-container--admin-autocomplete.select2-container--open .select2-selection {
border-color: var(--body-quiet-color);
min-height: 30px;
}
/* 聚焦或打开状态下单选选择器内边距 */
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--single,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--single {
padding: 0;
}
/* 聚焦或打开状态下多选选择器内边距 */
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--multiple,
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--multiple {
padding: 0;
}
/* 单选选择器背景、边框和圆角设置 */
.select2-container--admin-autocomplete .select2-selection--single {
background-color: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
}
/* 单选选择器渲染内容颜色和行高设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered {
color: var(--body-fg);
line-height: 30px;
}
/* 单选选择器清除按钮光标和浮动设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
}
/* 单选选择器占位符颜色设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder {
color: var(--body-quiet-color);
}
/* 单选选择器箭头高度、位置和宽度设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
}
/* 单选选择器箭头图标样式设置 */
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0;
}
/* 从右到左语言环境下单选选择器清除按钮浮动设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left;
}
/* 从右到左语言环境下单选选择器箭头位置设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto;
}
/* 禁用状态下单选选择器背景和光标设置 */
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single {
background-color: var(--darkened-bg);
cursor: default;
}
/* 禁用状态下单选选择器清除按钮显示设置 */
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none;
}
/* 打开状态下单选选择器箭头图标样式设置 */
.select2-container--admin-autocomplete.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px;
}
/* 多选选择器背景、边框、圆角和光标设置 */
.select2-container--admin-autocomplete .select2-selection--multiple {
background-color: var(--body-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: text;
}
/* 多选选择器渲染内容盒模型、列表样式、外边距、内边距和宽度设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 10px 5px 5px;
width: 100%;
display: flex;
flex-wrap: wrap;
}
/* 多选选择器渲染内容列表项样式设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li {
list-style: none;
}
/* 多选选择器占位符颜色、上外边距和浮动设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder {
color: var(--body-quiet-color);
margin-top: 5px;
float: left;
}
/* 多选选择器清除按钮光标、浮动、字体粗细、外边距、位置和右边距设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin: 5px;
position: absolute;
right: 0;
}
/* 多选选择器选项背景、边框、圆角、光标、浮动、右边距、上外边距和内边距设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice {
background-color: var(--darkened-bg);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px;
}
/* 多选选择器选项移除按钮颜色、光标、显示、字体粗细和右边距设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove {
color: var(--body-quiet-color);
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px;
}
/* 多选选择器选项移除按钮悬停状态颜色设置 */
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover {
color: var(--body-fg);
}
/* 从右到左语言环境下多选选择器选项、占位符和内联搜索浮动设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right;
}
/* 从右到左语言环境下多选选择器选项左边距和右边距设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto;
}
/* 从右到左语言环境下多选选择器选项移除按钮左边距和右边距设置 */
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto;
}
/* 聚焦状态下多选选择器边框设置 */
.select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple {
border: solid var(--body-quiet-color) 1px;
outline: 0;
}
/* 禁用状态下多选选择器背景和光标设置 */
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple {
background-color: var(--darkened-bg);
cursor: default;
}
/* 禁用状态下多选选择器选项移除按钮显示设置 */
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection__choice__remove {
display: none;
}
/* 打开状态下选择器上边框圆角设置 */
.select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
/* 打开状态下选择器下边框圆角设置 */
.select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
/* 下拉搜索框背景设置 */
.select2-container--admin-autocomplete .select2-search--dropdown {
background: var(--darkened-bg);
}
/* 下拉搜索框字段背景、颜色、边框、圆角设置 */
.select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field {
background: var(--body-bg);
color: var(--body-fg);
border: 1px solid var(--border-color);
border-radius: 4px;
}
/* 内联搜索框字段背景、颜色、边框、轮廓和外观设置 */
.select2-container--admin-autocomplete .select2-search--inline .select2-search__field {
background: transparent;
color: var(--body-fg);
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield;
}
/* 结果选项最大高度、溢出和颜色设置 */
.select2-container--admin-autocomplete .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto;
color: var(--body-fg);
background: var(--body-bg);
}
/* 分组结果选项内边距设置 */
.select2-container--admin-autocomplete .select2-results__option[role=group] {
padding: 0;
}
/* 禁用结果选项颜色设置 */
.select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] {
color: var(--body-quiet-color);
}
/* 选中结果选项背景和颜色设置 */
.select2-container--admin-autocomplete .select2-results__option[aria-selected=true] {
background-color: var(--selected-bg);
color: var(--body-fg);
}
/* 结果选项嵌套内边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option {
padding-left: 1em;
}
/* 结果选项嵌套分组内边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0;
}
/* 结果选项嵌套第二层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em;
}
/* 结果选项嵌套第三层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em;
}
/* 结果选项嵌套第四层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em;
}
/* 结果选项嵌套第五层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em;
}
/* 结果选项嵌套第六层内边距和左边距设置 */
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em;
}
/* 高亮结果选项背景和颜色设置 */
.select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] {
background-color: var(--primary);
color: var(--primary-fg);
}
/* 分组结果选项光标、显示、内边距设置 */
.select2-container--admin-autocomplete .select2-results__group {
cursor: default;
display: block;
padding: 6px;
}
/* 错误状态下选择器边框设置 */
.errors .select2-selection {
border: 1px solid var(--error-fg);
}

File diff suppressed because it is too large Load Diff

@ -1,350 +0,0 @@
/* 变更列表 */
/* CHANGELISTS */
#changelist {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
#changelist .changelist-form-container {
flex: 1 1 auto;
min-width: 0;
}
#changelist table {
width: 100%;
}
.change-list .hiddenfields { display:none; }
.change-list .filtered table {
border-right: none;
}
.change-list .filtered {
min-height: 400px;
}
.change-list .filtered .results, .change-list .filtered .paginator,
.filtered #toolbar, .filtered div.xfull {
width: auto;
}
.change-list .filtered table tbody th {
padding-right: 1em;
}
#changelist-form .results {
overflow-x: auto;
width: 100%;
}
#changelist .toplinks {
border-bottom: 1px solid var(--hairline-color);
}
#changelist .paginator {
color: var(--body-quiet-color);
border-bottom: 1px solid var(--hairline-color);
background: var(--body-bg);
overflow: hidden;
}
/* 变更列表表格 */
/* CHANGELIST TABLES */
#changelist table thead th {
padding: 0;
white-space: nowrap;
vertical-align: middle;
}
#changelist table thead th.action-checkbox-column {
width: 1.5em;
text-align: center;
}
#changelist table tbody td.action-checkbox {
text-align: center;
}
#changelist table tfoot {
color: var(--body-quiet-color);
}
/* 工具栏 */
/* TOOLBAR */
#toolbar {
padding: 8px 10px;
margin-bottom: 15px;
border-top: 1px solid var(--hairline-color);
border-bottom: 1px solid var(--hairline-color);
background: var(--darkened-bg);
color: var(--body-quiet-color);
}
#toolbar form input {
border-radius: 4px;
font-size: 0.875rem;
padding: 5px;
color: var(--body-fg);
}
#toolbar #searchbar {
height: 1.1875rem;
border: 1px solid var(--border-color);
padding: 2px 5px;
margin: 0;
vertical-align: top;
font-size: 0.8125rem;
max-width: 100%;
}
#toolbar #searchbar:focus {
border-color: var(--body-quiet-color);
}
#toolbar form input[type="submit"] {
border: 1px solid var(--border-color);
font-size: 0.8125rem;
padding: 4px 8px;
margin: 0;
vertical-align: middle;
background: var(--body-bg);
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
cursor: pointer;
color: var(--body-fg);
}
#toolbar form input[type="submit"]:focus,
#toolbar form input[type="submit"]:hover {
border-color: var(--body-quiet-color);
}
#changelist-search img {
vertical-align: middle;
margin-right: 4px;
}
#changelist-search .help {
word-break: break-word;
}
/* 过滤器列 */
/* FILTER COLUMN */
#changelist-filter {
flex: 0 0 240px;
order: 1;
background: var(--darkened-bg);
border-left: none;
margin: 0 0 0 30px;
}
@media (forced-colors: active) {
#changelist-filter {
border: 1px solid;
}
}
#changelist-filter h2 {
font-size: 0.875rem;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 5px 15px;
margin-bottom: 12px;
border-bottom: none;
}
#changelist-filter h3,
#changelist-filter details summary {
font-weight: 400;
padding: 0 15px;
margin-bottom: 10px;
}
#changelist-filter details summary > * {
display: inline;
}
#changelist-filter details > summary {
list-style-type: none;
}
#changelist-filter details > summary::-webkit-details-marker {
display: none;
}
#changelist-filter details > summary::before {
content: '→';
font-weight: bold;
color: var(--link-hover-color);
}
#changelist-filter details[open] > summary::before {
content: '↓';
}
#changelist-filter ul {
margin: 5px 0;
padding: 0 15px 15px;
border-bottom: 1px solid var(--hairline-color);
}
#changelist-filter ul:last-child {
border-bottom: none;
}
#changelist-filter li {
list-style-type: none;
margin-left: 0;
padding-left: 0;
}
#changelist-filter a {
display: block;
color: var(--body-quiet-color);
word-break: break-word;
}
#changelist-filter li.selected {
border-left: 5px solid var(--hairline-color);
padding-left: 10px;
margin-left: -15px;
}
#changelist-filter li.selected a {
color: var(--link-selected-fg);
}
#changelist-filter a:focus, #changelist-filter a:hover,
#changelist-filter li.selected a:focus,
#changelist-filter li.selected a:hover {
color: var(--link-hover-color);
}
#changelist-filter #changelist-filter-extra-actions {
font-size: 0.8125rem;
margin-bottom: 10px;
border-bottom: 1px solid var(--hairline-color);
}
/* 日期钻取 */
/* DATE DRILLDOWN */
.change-list .toplinks {
display: flex;
padding-bottom: 5px;
flex-wrap: wrap;
gap: 3px 17px;
font-weight: bold;
}
.change-list .toplinks a {
font-size: 0.8125rem;
}
.change-list .toplinks .date-back {
color: var(--body-quiet-color);
}
.change-list .toplinks .date-back:focus,
.change-list .toplinks .date-back:hover {
color: var(--link-hover-color);
}
/* 操作 */
/* ACTIONS */
.filtered .actions {
border-right: none;
}
#changelist table input {
margin: 0;
vertical-align: baseline;
}
/* 一旦所有浏览器都支持:has()伪类可以移除tr.selected选择器和添加该类的JS代码 */
/* Once the :has() pseudo-class is supported by all browsers, the tr.selected
selector and the JS adding the class can be removed. */
#changelist tbody tr.selected {
background-color: var(--selected-row);
}
#changelist tbody tr:has(.action-select:checked) {
background-color: var(--selected-row);
}
@media (forced-colors: active) {
#changelist tbody tr.selected {
background-color: SelectedItem;
}
#changelist tbody tr:has(.action-select:checked) {
background-color: SelectedItem;
}
}
#changelist .actions {
padding: 10px;
background: var(--body-bg);
border-top: none;
border-bottom: none;
line-height: 1.5rem;
color: var(--body-quiet-color);
width: 100%;
}
#changelist .actions span.all,
#changelist .actions span.action-counter,
#changelist .actions span.clear,
#changelist .actions span.question {
font-size: 0.8125rem;
margin: 0 0.5em;
}
#changelist .actions:last-child {
border-bottom: none;
}
#changelist .actions select {
vertical-align: top;
height: 1.5rem;
color: var(--body-fg);
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 0.875rem;
padding: 0 0 0 4px;
margin: 0;
margin-left: 10px;
}
#changelist .actions select:focus {
border-color: var(--body-quiet-color);
}
#changelist .actions label {
display: inline-block;
vertical-align: middle;
font-size: 0.8125rem;
}
#changelist .actions .button {
font-size: 0.8125rem;
border: 1px solid var(--border-color);
border-radius: 4px;
background: var(--body-bg);
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
cursor: pointer;
height: 1.5rem;
line-height: 1;
padding: 4px 8px;
margin: 0;
color: var(--body-fg);
}
#changelist .actions .button:focus, #changelist .actions .button:hover {
border-color: var(--body-quiet-color);
}

@ -1,221 +0,0 @@
/* 深色模式媒体查询,当系统偏好设置为深色时应用 */
/* Dark mode media query, applied when system preference is set to dark */
@media (prefers-color-scheme: dark) {
:root {
/* 主色调 */
/* Primary color */
--primary: #264b5d;
/* 主要前景色 */
/* Primary foreground color */
--primary-fg: #f7f7f7;
/* 主体前景色 */
/* Body foreground color */
--body-fg: #eeeeee;
/* 主体背景色 */
/* Body background color */
--body-bg: #121212;
/* 主体安静色(较淡)*/
/* Body quiet color (lighter) */
--body-quiet-color: #d0d0d0;
/* 主体中等颜色 */
/* Body medium color */
--body-medium-color: #e0e0e0;
/* 主体响亮色(较亮)*/
/* Body loud color (brighter) */
--body-loud-color: #ffffff;
/* 面包屑链接颜色 */
/* Breadcrumbs link color */
--breadcrumbs-link-fg: #e0e0e0;
/* 面包屑背景色 */
/* Breadcrumbs background color */
--breadcrumbs-bg: var(--primary);
/* 链接颜色 */
/* Link color */
--link-fg: #81d4fa;
/* 链接悬停颜色 */
/* Link hover color */
--link-hover-color: #4ac1f7;
/* 链接选中颜色 */
/* Link selected color */
--link-selected-fg: #6f94c6;
/* 细线颜色 */
/* Hairline color */
--hairline-color: #272727;
/* 边框颜色 */
/* Border color */
--border-color: #353535;
/* 错误颜色 */
/* Error color */
--error-fg: #e35f5f;
/* 成功消息背景色 */
/* Success message background color */
--message-success-bg: #006b1b;
/* 警告消息背景色 */
/* Warning message background color */
--message-warning-bg: #583305;
/* 错误消息背景色 */
/* Error message background color */
--message-error-bg: #570808;
/* 深色背景 */
/* Darkened background */
--darkened-bg: #212121;
/* 选中背景色 */
/* Selected background color */
--selected-bg: #1b1b1b;
/* 选中行颜色 */
/* Selected row color */
--selected-row: #00363a;
/* 关闭按钮背景色 */
/* Close button background color */
--close-button-bg: #333333;
/* 关闭按钮悬停背景色 */
/* Close button hover background color */
--close-button-hover-bg: #666666;
/* 颜色方案设置为深色 */
/* Color scheme set to dark */
color-scheme: dark;
}
}
/* 当HTML元素的data-theme属性为"dark"时应用的样式 */
/* Styles applied when HTML element's data-theme attribute is "dark" */
html[data-theme="dark"] {
--primary: #264b5d;
--primary-fg: #f7f7f7;
--body-fg: #eeeeee;
--body-bg: #121212;
--body-quiet-color: #d0d0d0;
--body-medium-color: #e0e0e0;
--body-loud-color: #ffffff;
--breadcrumbs-link-fg: #e0e0e0;
--breadcrumbs-bg: var(--primary);
--link-fg: #81d4fa;
--link-hover-color: #4ac1f7;
--link-selected-fg: #6f94c6;
--hairline-color: #272727;
--border-color: #353535;
--error-fg: #e35f5f;
--message-success-bg: #006b1b;
--message-warning-bg: #583305;
--message-error-bg: #570808;
--darkened-bg: #212121;
--selected-bg: #1b1b1b;
--selected-row: #00363a;
--close-button-bg: #333333;
--close-button-hover-bg: #666666;
color-scheme: dark;
}
/* 主题切换按钮样式 */
/* Theme switch button styles */
.theme-toggle {
/* 设置光标为指针 */
/* Set cursor to pointer */
cursor: pointer;
/* 移除边框 */
/* Remove border */
border: none;
/* 设置内边距为0 */
/* Set padding to 0 */
padding: 0;
/* 设置背景为透明 */
/* Set background to transparent */
background: transparent;
/* 设置垂直对齐方式 */
/* Set vertical alignment */
vertical-align: middle;
/* 设置左边距 */
/* Set left margin */
margin-inline-start: 5px;
/* 设置上边距 */
/* Set top margin */
margin-top: -1px;
}
/* 主题切换按钮中的SVG图标样式 */
/* SVG icon styles in theme toggle button */
.theme-toggle svg {
/* 设置垂直对齐方式 */
/* Set vertical alignment */
vertical-align: middle;
/* 设置高度 */
/* Set height */
height: 1.5rem;
/* 设置宽度 */
/* Set width */
width: 1.5rem;
/* 默认不显示 */
/* Hidden by default */
display: none;
}
/*
Fully hide screen reader text so we only show the one matching the current
theme.
*/
.theme-toggle .visually-hidden {
display: none;
}
/* 当data-theme属性为"auto"时显示自动主题标签 */
/* Show auto theme label when data-theme attribute is "auto" */
html[data-theme="auto"] .theme-toggle .theme-label-when-auto {
display: block;
}
/* 当data-theme属性为"dark"时显示深色主题标签 */
/* Show dark theme label when data-theme attribute is "dark" */
html[data-theme="dark"] .theme-toggle .theme-label-when-dark {
display: block;
}
/* 当data-theme属性为"light"时显示浅色主题标签 */
/* Show light theme label when data-theme attribute is "light" */
html[data-theme="light"] .theme-toggle .theme-label-when-light {
display: block;
}
/* 图标样式 */
/* Icon styles */
.theme-toggle svg.theme-icon-when-auto,
.theme-toggle svg.theme-icon-when-dark,
.theme-toggle svg.theme-icon-when-light {
fill: var(--header-link-color);
color: var(--header-bg);
}
/* 当data-theme属性为"auto"时显示自动主题图标 */
/* Show auto theme icon when data-theme attribute is "auto" */
html[data-theme="auto"] .theme-toggle svg.theme-icon-when-auto {
display: block;
}
/* 当data-theme属性为"dark"时显示深色主题图标 */
/* Show dark theme icon when data-theme attribute is "dark" */
html[data-theme="dark"] .theme-toggle svg.theme-icon-when-dark {
display: block;
}
/* 当data-theme属性为"light"时显示浅色主题图标 */
/* Show light theme icon when data-theme attribute is "light" */
html[data-theme="light"] .theme-toggle svg.theme-icon-when-light {
display: block;
}

@ -1,31 +0,0 @@
/* 仪表板 */
/* DASHBOARD */
.dashboard td, .dashboard th {
word-break: break-word;
}
.dashboard .module table th {
width: 100%;
}
.dashboard .module table td {
white-space: nowrap;
}
.dashboard .module table td a {
display: block;
padding-right: .6em;
}
/* 最近操作模块 */
/* RECENT ACTIONS MODULE */
.module ul.actionlist {
margin-left: 0;
}
ul.actionlist li {
list-style-type: none;
overflow: hidden;
text-overflow: ellipsis;
}

@ -1,511 +0,0 @@
/* 导入小部件样式 */
@import url('widgets.css');
/* 表单行 */
/* FORM ROWS */
.form-row {
overflow: hidden;
padding: 10px;
font-size: 0.8125rem;
border-bottom: 1px solid var(--hairline-color);
}
.form-row img, .form-row input {
vertical-align: middle;
}
.form-row label input[type="checkbox"] {
margin-top: 0;
vertical-align: 0;
}
form .form-row p {
padding-left: 0;
}
.flex-container {
display: flex;
}
.form-multiline {
flex-wrap: wrap;
}
.form-multiline > div {
padding-bottom: 10px;
}
/* 表单标签 */
/* FORM LABELS */
label {
font-weight: normal;
color: var(--body-quiet-color);
font-size: 0.8125rem;
}
.required label, label.required {
font-weight: bold;
}
/* 单选按钮 */
/* RADIO BUTTONS */
form div.radiolist div {
padding-right: 7px;
}
form div.radiolist.inline div {
display: inline-block;
}
form div.radiolist label {
width: auto;
}
form div.radiolist input[type="radio"] {
margin: -2px 4px 0 0;
padding: 0;
}
form ul.inline {
margin-left: 0;
padding: 0;
}
form ul.inline li {
float: left;
padding-right: 7px;
}
/* 字段集 */
/* FIELDSETS */
fieldset .fieldset-heading,
fieldset .inline-heading,
:not(.inline-related) .collapse summary {
border: 1px solid var(--header-bg);
margin: 0;
padding: 8px;
font-weight: 400;
font-size: 0.8125rem;
background: var(--header-bg);
color: var(--header-link-color);
}
/* 对齐的字段集 */
/* ALIGNED FIELDSETS */
.aligned label {
display: block;
padding: 4px 10px 0 0;
min-width: 160px;
width: 160px;
word-wrap: break-word;
}
.aligned label:not(.vCheckboxLabel):after {
content: '';
display: inline-block;
vertical-align: middle;
}
.aligned label + p, .aligned .checkbox-row + div.help, .aligned label + div.readonly {
padding: 6px 0;
margin-top: 0;
margin-bottom: 0;
margin-left: 0;
overflow-wrap: break-word;
}
.aligned ul label {
display: inline;
float: none;
width: auto;
}
.aligned .form-row input {
margin-bottom: 0;
}
.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
width: 350px;
}
form .aligned ul {
margin-left: 160px;
padding-left: 10px;
}
form .aligned div.radiolist {
display: inline-block;
margin: 0;
padding: 0;
}
form .aligned p.help,
form .aligned div.help {
margin-top: 0;
margin-left: 160px;
padding-left: 10px;
}
form .aligned p.date div.help.timezonewarning,
form .aligned p.datetime div.help.timezonewarning,
form .aligned p.time div.help.timezonewarning {
margin-left: 0;
padding-left: 0;
font-weight: normal;
}
form .aligned p.help:last-child,
form .aligned div.help:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
form .aligned input + p.help,
form .aligned textarea + p.help,
form .aligned select + p.help,
form .aligned input + div.help,
form .aligned textarea + div.help,
form .aligned select + div.help {
margin-left: 160px;
padding-left: 10px;
}
form .aligned select option:checked {
background-color: var(--selected-row);
}
form .aligned ul li {
list-style: none;
}
form .aligned table p {
margin-left: 0;
padding-left: 0;
}
.aligned .vCheckboxLabel {
padding: 1px 0 0 5px;
}
.aligned .vCheckboxLabel + p.help,
.aligned .vCheckboxLabel + div.help {
margin-top: -4px;
}
.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
width: 610px;
}
fieldset .fieldBox {
margin-right: 20px;
}
/* 宽字段集 */
/* WIDE FIELDSETS */
.wide label {
width: 200px;
}
form .wide p.help,
form .wide ul.errorlist,
form .wide div.help {
padding-left: 50px;
}
form div.help ul {
padding-left: 0;
margin-left: 0;
}
.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
width: 450px;
}
/* 可折叠字段集 */
/* COLLAPSIBLE FIELDSETS */
.collapse summary .fieldset-heading,
.collapse summary .inline-heading {
background: transparent;
border: none;
color: currentColor;
display: inline;
margin: 0;
padding: 0;
}
/* 等宽字体文本区域 */
/* MONOSPACE TEXTAREAS */
fieldset.monospace textarea {
font-family: var(--font-family-monospace);
}
/* 提交行 */
/* SUBMIT ROW */
.submit-row {
padding: 12px 14px 12px;
margin: 0 0 20px;
background: var(--darkened-bg);
border: 1px solid var(--hairline-color);
border-radius: 4px;
overflow: hidden;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
body.popup .submit-row {
overflow: auto;
}
.submit-row input {
height: 2.1875rem;
line-height: 0.9375rem;
}
.submit-row input, .submit-row a {
margin: 0;
}
.submit-row input.default {
text-transform: uppercase;
}
.submit-row a.deletelink {
margin-left: auto;
}
.submit-row a.deletelink {
display: block;
background: var(--delete-button-bg);
border-radius: 4px;
padding: 0.625rem 0.9375rem;
height: 0.9375rem;
line-height: 0.9375rem;
color: var(--button-fg);
}
.submit-row a.closelink {
display: inline-block;
background: var(--close-button-bg);
border-radius: 4px;
padding: 10px 15px;
height: 0.9375rem;
line-height: 0.9375rem;
color: var(--button-fg);
}
.submit-row a.deletelink:focus,
.submit-row a.deletelink:hover,
.submit-row a.deletelink:active {
background: var(--delete-button-hover-bg);
text-decoration: none;
}
.submit-row a.closelink:focus,
.submit-row a.closelink:hover,
.submit-row a.closelink:active {
background: var(--close-button-hover-bg);
text-decoration: none;
}
/* 自定义表单字段 */
/* CUSTOM FORM FIELDS */
.vSelectMultipleField {
vertical-align: top;
}
.vCheckboxField {
border: none;
}
.vDateField, .vTimeField {
margin-right: 2px;
margin-bottom: 4px;
}
.vDateField {
min-width: 6.85em;
}
.vTimeField {
min-width: 4.7em;
}
.vURLField {
width: 30em;
}
.vLargeTextField, .vXMLLargeTextField {
width: 48em;
}
.flatpages-flatpage #id_content {
height: 40.2em;
}
.module table .vPositiveSmallIntegerField {
width: 2.2em;
}
.vIntegerField {
width: 5em;
}
.vBigIntegerField {
width: 10em;
}
.vForeignKeyRawIdAdminField {
width: 5em;
}
.vTextField, .vUUIDField {
width: 20em;
}
/* 行内组 */
/* INLINES */
.inline-group {
padding: 0;
margin: 0 0 30px;
}
.inline-group thead th {
padding: 8px 10px;
}
.inline-group .aligned label {
width: 160px;
}
.inline-related {
position: relative;
}
.inline-related h4,
.inline-related:not(.tabular) .collapse summary {
margin: 0;
color: var(--body-medium-color);
padding: 5px;
font-size: 0.8125rem;
background: var(--darkened-bg);
border: 1px solid var(--hairline-color);
border-left-color: var(--darkened-bg);
border-right-color: var(--darkened-bg);
}
.inline-related h3 span.delete {
float: right;
}
.inline-related h3 span.delete label {
margin-left: 2px;
font-size: 0.6875rem;
}
.inline-related fieldset {
margin: 0;
background: var(--body-bg);
border: none;
width: 100%;
}
.inline-group .tabular fieldset.module {
border: none;
}
.inline-related.tabular fieldset.module table {
width: 100%;
overflow-x: scroll;
}
.last-related fieldset {
border: none;
}
.inline-group .tabular tr.has_original td {
padding-top: 2em;
}
.inline-group .tabular tr td.original {
padding: 2px 0 0 0;
width: 0;
_position: relative;
}
.inline-group .tabular th.original {
width: 0px;
padding: 0;
}
.inline-group .tabular td.original p {
position: absolute;
left: 0;
height: 1.1em;
padding: 2px 9px;
overflow: hidden;
font-size: 0.5625rem;
font-weight: bold;
color: var(--body-quiet-color);
_width: 700px;
}
.inline-group div.add-row,
.inline-group .tabular tr.add-row td {
color: var(--body-quiet-color);
background: var(--darkened-bg);
padding: 8px 10px;
border-bottom: 1px solid var(--hairline-color);
}
.inline-group .tabular tr.add-row td {
padding: 8px 10px;
border-bottom: 1px solid var(--hairline-color);
}
.inline-group div.add-row a,
.inline-group .tabular tr.add-row td a {
font-size: 0.75rem;
}
.empty-form {
display: none;
}
/* 相关字段添加一个/查找 */
/* RELATED FIELD ADD ONE / LOOKUP */
.related-lookup {
margin-left: 5px;
display: inline-block;
vertical-align: middle;
background-repeat: no-repeat;
background-size: 14px;
}
.related-lookup {
width: 1rem;
height: 1rem;
background-image: url(../img/search.svg);
}
form .related-widget-wrapper ul {
display: inline-block;
margin-left: 0;
padding-left: 0;
}
.clearable-file-input input {
margin-top: 0;
}

@ -1,89 +0,0 @@
/* 登录表单样式 */
/* LOGIN FORM */
.login {
/* 设置背景色 */
background: var(--darkened-bg);
/* 设置高度为自动 */
height: auto;
}
.login #header {
/* 设置头部高度为自动 */
height: auto;
/* 设置内边距 */
padding: 15px 16px;
/* 设置内容居中对齐 */
justify-content: center;
}
.login #header h1 {
/* 设置标题字体大小 */
font-size: 1.125rem;
/* 设置外边距 */
margin: 0;
}
.login #header h1 a {
/* 设置链接颜色 */
color: var(--header-link-color);
}
.login #content {
/* 设置内容区内边距 */
padding: 20px;
}
.login #container {
/* 设置容器背景色 */
background: var(--body-bg);
/* 设置边框 */
border: 1px solid var(--hairline-color);
/* 设置圆角 */
border-radius: 4px;
/* 设置溢出隐藏 */
overflow: hidden;
/* 设置宽度 */
width: 28em;
/* 设置最小宽度 */
min-width: 300px;
/* 设置外边距居中 */
margin: 100px auto;
/* 设置高度为自动 */
height: auto;
}
.login .form-row {
/* 设置表单行内边距 */
padding: 4px 0;
}
.login .form-row label {
/* 设置标签为块级元素 */
display: block;
/* 设置行高 */
line-height: 2em;
}
.login .form-row #id_username, .login .form-row #id_password {
/* 设置内边距 */
padding: 8px;
/* 设置宽度为100% */
width: 100%;
/* 设置盒模型为border-box */
box-sizing: border-box;
}
.login .submit-row {
/* 设置提交行内边距 */
padding: 1em 0 0 0;
/* 设置外边距 */
margin: 0;
/* 设置文本居中对齐 */
text-align: center;
}
.login .password-reset-link {
/* 设置密码重置链接文本居中对齐 */
text-align: center;
}

@ -1,178 +0,0 @@
/* 粘性定位 - 使侧边栏在滚动时保持在顶部 */
/* sticky */
.sticky {
position: sticky;
top: 0;
max-height: 100vh;
}
/* 切换导航侧边栏按钮 */
/* 切换导航侧边栏按钮 */
.toggle-nav-sidebar {
z-index: 20;
left: 0;
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 23px;
width: 23px;
border: 0;
border-right: 1px solid var(--hairline-color);
background-color: var(--body-bg);
cursor: pointer;
font-size: 1.25rem;
color: var(--link-fg);
padding: 0;
}
/* 从右到左语言环境下的切换按钮边框调整 */
[dir="rtl"] .toggle-nav-sidebar {
border-left: 1px solid var(--hairline-color);
border-right: 0;
}
/* 切换按钮的悬停和焦点状态 */
.toggle-nav-sidebar:hover,
.toggle-nav-sidebar:focus {
background-color: var(--darkened-bg);
}
/* 导航侧边栏 */
#nav-sidebar {
z-index: 15;
flex: 0 0 275px;
left: -276px;
margin-left: -276px;
border-top: 1px solid transparent;
border-right: 1px solid var(--hairline-color);
background-color: var(--body-bg);
overflow: auto;
}
/* 从右到左语言环境下的导航侧边栏调整 */
[dir="rtl"] #nav-sidebar {
border-left: 1px solid var(--hairline-color);
border-right: 0;
left: 0;
margin-left: 0;
right: -276px;
margin-right: -276px;
}
/* 切换导航侧边栏按钮的:before伪元素内容 */
.toggle-nav-sidebar::before {
content: '\00BB';
}
/* 主内容区域移动时切换按钮的:before伪元素内容 */
.main.shifted .toggle-nav-sidebar::before {
content: '\00AB';
}
/* 主内容区域的导航侧边栏可见性 */
.main > #nav-sidebar {
visibility: hidden;
}
/* 主内容区域移动时导航侧边栏的可见性 */
.main.shifted > #nav-sidebar {
margin-left: 0;
visibility: visible;
}
/* 从右到左语言环境下主内容区域移动时导航侧边栏的右边距 */
[dir="rtl"] .main.shifted > #nav-sidebar {
margin-right: 0;
}
/* 导航侧边栏模块标题的宽度和换行处理 */
#nav-sidebar .module th {
width: 100%;
overflow-wrap: anywhere;
}
/* 导航侧边栏模块标题和说明文字的内边距 */
#nav-sidebar .module th,
#nav-sidebar .module caption {
padding-left: 16px;
}
/* 导航侧边栏模块数据单元格的空白处理 */
#nav-sidebar .module td {
white-space: nowrap;
}
/* 从右到左语言环境下导航侧边栏模块标题和说明文字的内边距调整 */
[dir="rtl"] #nav-sidebar .module th,
[dir="rtl"] #nav-sidebar .module caption {
padding-left: 8px;
padding-right: 16px;
}
/* 当前应用的节链接样式 */
#nav-sidebar .current-app .section:link,
#nav-sidebar .current-app .section:visited {
color: var(--header-color);
font-weight: bold;
}
/* 当前模型的背景色 */
#nav-sidebar .current-model {
background: var(--selected-row);
}
/* 强制颜色模式下当前模型的背景色 */
@media (forced-colors: active) {
#nav-sidebar .current-model {
background-color: SelectedItem;
}
}
/* 主内容区域导航侧边栏和内容的最大宽度 */
.main > #nav-sidebar + .content {
max-width: calc(100% - 23px);
}
/* 主内容区域移动时导航侧边栏和内容的最大宽度 */
.main.shifted > #nav-sidebar + .content {
max-width: calc(100% - 299px);
}
/* 移动设备样式 - 隐藏导航侧边栏和切换按钮 */
@media (max-width: 767px) {
#nav-sidebar, #toggle-nav-sidebar {
display: none;
}
/* 移动设备上主内容区域的最大宽度 */
.main > #nav-sidebar + .content,
.main.shifted > #nav-sidebar + .content {
max-width: 100%;
}
}
/* 导航过滤器输入框样式 */
#nav-filter {
width: 100%;
box-sizing: border-box;
padding: 2px 5px;
margin: 5px 0;
border: 1px solid var(--border-color);
background-color: var(--darkened-bg);
color: var(--body-fg);
}
/* 导航过滤器输入框的焦点状态 */
#nav-filter:focus {
border-color: var(--body-quiet-color);
}
/* 导航过滤器无结果时的背景色 */
#nav-filter.no-results {
background: var(--message-error-bg);
}
/* 导航侧边栏表格宽度 */
#nav-sidebar table {
width: 100%;
}

@ -1,930 +0,0 @@
/* 平板设备 */
/* Tablets */
input[type="submit"], button {
-webkit-appearance: none;
appearance: none;
}
@media (max-width: 1024px) {
/* 基础样式 */
/* Basic */
html {
-webkit-text-size-adjust: 100%;
}
td, th {
padding: 10px;
font-size: 0.875rem;
}
.small {
font-size: 0.75rem;
}
/* 布局 */
/* Layout */
#container {
min-width: 0;
}
#content {
padding: 15px 20px 20px;
}
div.breadcrumbs {
padding: 10px 30px;
}
/* 头部 */
/* Header */
#header {
flex-direction: column;
padding: 15px 30px;
justify-content: flex-start;
}
#site-name {
margin: 0 0 8px;
line-height: 1.2;
}
#user-tools {
margin: 0;
font-weight: 400;
line-height: 1.85;
text-align: left;
}
#user-tools a {
display: inline-block;
line-height: 1.4;
}
/* 仪表板 */
/* Dashboard */
.dashboard #content {
width: auto;
}
#content-related {
margin-right: -290px;
}
.colSM #content-related {
margin-left: -290px;
}
.colMS {
margin-right: 290px;
}
.colSM {
margin-left: 290px;
}
.dashboard .module table td a {
padding-right: 0;
}
td .changelink, td .addlink {
font-size: 0.8125rem;
}
/* 变更列表 */
/* Changelist */
#toolbar {
border: none;
padding: 15px;
}
#changelist-search > div {
display: flex;
flex-wrap: nowrap;
max-width: 480px;
}
#changelist-search label {
line-height: 1.375rem;
}
#toolbar form #searchbar {
flex: 1 0 auto;
width: 0;
height: 1.375rem;
margin: 0 10px 0 6px;
}
#toolbar form input[type=submit] {
flex: 0 1 auto;
}
#changelist-search .quiet {
width: 0;
flex: 1 0 auto;
margin: 5px 0 0 25px;
}
#changelist .actions {
display: flex;
flex-wrap: wrap;
padding: 15px 0;
}
#changelist .actions label {
display: flex;
}
#changelist .actions select {
background: var(--body-bg);
}
#changelist .actions .button {
min-width: 48px;
margin: 0 10px;
}
#changelist .actions span.all,
#changelist .actions span.clear,
#changelist .actions span.question,
#changelist .actions span.action-counter {
font-size: 0.6875rem;
margin: 0 10px 0 0;
}
#changelist-filter {
flex-basis: 200px;
}
.change-list .filtered .results,
.change-list .filtered .paginator,
.filtered #toolbar,
.filtered .actions,
#changelist .paginator {
border-top-color: var(--hairline-color); /* XXX Is this used at all? */
}
#changelist .results + .paginator {
border-top: none;
}
/* 表单 */
/* Forms */
label {
font-size: 1rem;
}
/*
"input" HTML"type"
input:not([type])使CSS
*/
.form-row input:not([type]),
.form-row input[type=text],
.form-row input[type=password],
.form-row input[type=email],
.form-row input[type=url],
.form-row input[type=tel],
.form-row input[type=number],
.form-row textarea,
.form-row select,
.form-row .vTextField {
box-sizing: border-box;
margin: 0;
padding: 6px 8px;
min-height: 2.25rem;
font-size: 1rem;
}
.form-row select {
height: 2.25rem;
}
.form-row select[multiple] {
height: auto;
min-height: 0;
}
fieldset .fieldBox + .fieldBox {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid var(--hairline-color);
}
textarea {
max-width: 100%;
max-height: 120px;
}
.aligned label {
padding-top: 6px;
}
.aligned .related-lookup,
.aligned .datetimeshortcuts,
.aligned .related-lookup + strong {
align-self: center;
margin-left: 15px;
}
form .aligned div.radiolist {
margin-left: 2px;
}
.submit-row {
padding: 8px;
}
.submit-row a.deletelink {
padding: 10px 7px;
}
.button, input[type=submit], input[type=button], .submit-row input, a.button {
padding: 7px;
}
/* 选择器 */
/* Selector */
.selector {
display: flex;
width: 100%;
}
.selector .selector-filter {
display: flex;
align-items: center;
}
.selector .selector-filter input {
width: 100%;
min-height: 0;
flex: 1 1;
}
.selector-available, .selector-chosen {
width: auto;
flex: 1 1;
display: flex;
flex-direction: column;
}
.selector select {
width: 100%;
flex: 1 0 auto;
margin-bottom: 5px;
}
.selector-chooseall, .selector-clearall {
align-self: center;
}
.stacked {
flex-direction: column;
max-width: 480px;
}
.stacked > * {
flex: 0 1 auto;
}
.stacked select {
margin-bottom: 0;
}
.stacked .selector-available, .stacked .selector-chosen {
width: auto;
}
.stacked ul.selector-chooser {
padding: 0 2px;
transform: none;
}
.stacked .selector-chooser li {
padding: 3px;
}
.help-tooltip, .selector .help-icon {
display: none;
}
.datetime input {
width: 50%;
max-width: 120px;
}
.datetime span {
font-size: 0.8125rem;
}
.datetime .timezonewarning {
display: block;
font-size: 0.6875rem;
color: var(--body-quiet-color);
}
.datetimeshortcuts {
color: var(--border-color); /* XXX Redundant, .datetime span also sets #ccc */
}
.form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
width: 75%;
}
.inline-group {
overflow: auto;
}
/* 消息 */
/* Messages */
ul.messagelist li {
padding-left: 55px;
background-position: 30px 12px;
}
ul.messagelist li.error {
background-position: 30px 12px;
}
ul.messagelist li.warning {
background-position: 30px 14px;
}
/* 登录 */
/* Login */
.login #header {
padding: 15px 20px;
}
.login #site-name {
margin: 0;
}
/* GIS */
div.olMap {
max-width: calc(100vw - 30px);
max-height: 300px;
}
.olMap + .clear_features {
display: block;
margin-top: 10px;
}
/* 文档 */
/* Docs */
.module table.xfull {
width: 100%;
}
pre.literal-block {
overflow: auto;
}
}
/* 移动设备 */
/* Mobile */
@media (max-width: 767px) {
/* 布局 */
/* Layout */
#header, #content {
padding: 15px;
}
div.breadcrumbs {
padding: 10px 15px;
}
/* 仪表板 */
/* Dashboard */
.colMS, .colSM {
margin: 0;
}
#content-related, .colSM #content-related {
width: 100%;
margin: 0;
}
#content-related .module {
margin-bottom: 0;
}
#content-related .module h2 {
padding: 10px 15px;
font-size: 1rem;
}
/* 变更列表 */
/* Changelist */
#changelist {
align-items: stretch;
flex-direction: column;
}
#toolbar {
padding: 10px;
}
#changelist-filter {
margin-left: 0;
}
#changelist .actions label {
flex: 1 1;
}
#changelist .actions select {
flex: 1 0;
width: 100%;
}
#changelist .actions span {
flex: 1 0 100%;
}
#changelist-filter {
position: static;
width: auto;
margin-top: 30px;
}
.object-tools {
float: none;
margin: 0 0 15px;
padding: 0;
overflow: hidden;
}
.object-tools li {
height: auto;
margin-left: 0;
}
.object-tools li + li {
margin-left: 15px;
}
/* 表单 */
/* Forms */
.form-row {
padding: 15px 0;
}
.aligned .form-row,
.aligned .form-row > div {
max-width: 100vw;
}
.aligned .form-row > div {
width: calc(100vw - 30px);
}
.flex-container {
flex-flow: column;
}
.flex-container.checkbox-row {
flex-flow: row;
}
textarea {
max-width: none;
}
.vURLField {
width: auto;
}
fieldset .fieldBox + .fieldBox {
margin-top: 15px;
padding-top: 15px;
}
.aligned label {
width: 100%;
min-width: auto;
padding: 0 0 10px;
}
.aligned label:after {
max-height: 0;
}
.aligned .form-row input,
.aligned .form-row select,
.aligned .form-row textarea {
flex: 1 1 auto;
max-width: 100%;
}
.aligned .checkbox-row input {
flex: 0 1 auto;
margin: 0;
}
.aligned .vCheckboxLabel {
flex: 1 0;
padding: 1px 0 0 5px;
}
.aligned label + p,
.aligned label + div.help,
.aligned label + div.readonly {
padding: 0;
margin-left: 0;
}
.aligned p.file-upload {
font-size: 0.8125rem;
}
span.clearable-file-input {
margin-left: 15px;
}
span.clearable-file-input label {
font-size: 0.8125rem;
padding-bottom: 0;
}
.aligned .timezonewarning {
flex: 1 0 100%;
margin-top: 5px;
}
form .aligned .form-row div.help {
width: 100%;
margin: 5px 0 0;
padding: 0;
}
form .aligned ul,
form .aligned ul.errorlist {
margin-left: 0;
padding-left: 0;
}
form .aligned div.radiolist {
margin-top: 5px;
margin-right: 15px;
margin-bottom: -3px;
}
form .aligned div.radiolist:not(.inline) div + div {
margin-top: 5px;
}
/* 相关小部件 */
/* Related widget */
.related-widget-wrapper {
width: 100%;
display: flex;
align-items: flex-start;
}
.related-widget-wrapper .selector {
order: 1;
flex: 1 0 auto;
}
.related-widget-wrapper > a {
order: 2;
}
.related-widget-wrapper .radiolist ~ a {
align-self: flex-end;
}
.related-widget-wrapper > select ~ a {
align-self: center;
}
/* 选择器 */
/* Selector */
.selector {
flex-direction: column;
gap: 10px 0;
}
.selector-available, .selector-chosen {
flex: 1 1 auto;
}
.selector select {
max-height: 96px;
}
.selector ul.selector-chooser {
display: flex;
width: 60px;
height: 30px;
padding: 0 2px;
transform: none;
}
.selector ul.selector-chooser li {
float: left;
}
.selector-remove {
background-position: 0 0;
}
:enabled.selector-remove:focus, :enabled.selector-remove:hover {
background-position: 0 -24px;
}
.selector-add {
background-position: 0 -48px;
}
:enabled.selector-add:focus, :enabled.selector-add:hover {
background-position: 0 -72px;
}
/* 行内组 */
/* Inlines */
.inline-group[data-inline-type="stacked"] .inline-related {
border: 1px solid var(--hairline-color);
border-radius: 4px;
margin-top: 15px;
overflow: auto;
}
.inline-group[data-inline-type="stacked"] .inline-related > * {
box-sizing: border-box;
}
.inline-group[data-inline-type="stacked"] .inline-related .module {
padding: 0 10px;
}
.inline-group[data-inline-type="stacked"] .inline-related .module .form-row {
border-top: 1px solid var(--hairline-color);
border-bottom: none;
}
.inline-group[data-inline-type="stacked"] .inline-related .module .form-row:first-child {
border-top: none;
}
.inline-group[data-inline-type="stacked"] .inline-related h3 {
padding: 10px;
border-top-width: 0;
border-bottom-width: 2px;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.inline-group[data-inline-type="stacked"] .inline-related h3 .inline_label {
margin-right: auto;
}
.inline-group[data-inline-type="stacked"] .inline-related h3 span.delete {
float: none;
flex: 1 1 100%;
margin-top: 5px;
}
.inline-group[data-inline-type="stacked"] .aligned .form-row > div:not([class]) {
width: 100%;
}
.inline-group[data-inline-type="stacked"] .aligned label {
width: 100%;
}
.inline-group[data-inline-type="stacked"] div.add-row {
margin-top: 15px;
border: 1px solid var(--hairline-color);
border-radius: 4px;
}
.inline-group div.add-row,
.inline-group .tabular tr.add-row td {
padding: 0;
}
.inline-group div.add-row a,
.inline-group .tabular tr.add-row td a {
display: block;
padding: 8px 10px 8px 26px;
background-position: 8px 9px;
}
/* 提交行 */
/* Submit row */
.submit-row {
padding: 10px;
margin: 0 0 15px;
flex-direction: column;
gap: 8px;
}
.submit-row input, .submit-row input.default, .submit-row a {
text-align: center;
}
.submit-row a.closelink {
padding: 10px 0;
text-align: center;
}
.submit-row a.deletelink {
margin: 0;
}
/* 消息 */
/* Messages */
ul.messagelist li {
padding-left: 40px;
background-position: 15px 12px;
}
ul.messagelist li.error {
background-position: 15px 12px;
}
ul.messagelist li.warning {
background-position: 15px 14px;
}
/* 分页器 */
/* Paginator */
.paginator .this-page, .paginator a:link, .paginator a:visited {
padding: 4px 10px;
}
/* 登录 */
/* Login */
body.login {
padding: 0 15px;
}
.login #container {
width: auto;
max-width: 480px;
margin: 50px auto;
}
.login #header,
.login #content {
padding: 15px;
}
.login #content-main {
float: none;
}
.login .form-row {
padding: 0;
}
.login .form-row + .form-row {
margin-top: 15px;
}
.login .form-row label {
margin: 0 0 5px;
line-height: 1.2;
}
.login .submit-row {
padding: 15px 0 0;
}
.login br {
display: none;
}
.login .submit-row input {
margin: 0;
text-transform: uppercase;
}
.errornote {
margin: 0 0 20px;
padding: 8px 12px;
font-size: 0.8125rem;
}
/* 日历和时钟 */
/* Calendar and clock */
.calendarbox, .clockbox {
position: fixed !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%);
margin: 0;
border: none;
overflow: visible;
}
.calendarbox:before, .clockbox:before {
content: '';
position: fixed;
top: 50%;
left: 50%;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.75);
transform: translate(-50%, -50%);
}
.calendarbox > *, .clockbox > * {
position: relative;
z-index: 1;
}
.calendarbox > div:first-child {
z-index: 2;
}
.calendarbox .calendar, .clockbox h2 {
border-radius: 4px 4px 0 0;
overflow: hidden;
}
.calendarbox .calendar-cancel, .clockbox .calendar-cancel {
border-radius: 0 0 4px 4px;
overflow: hidden;
}
.calendar-shortcuts {
padding: 10px 0;
font-size: 0.75rem;
line-height: 0.75rem;
}
.calendar-shortcuts a {
margin: 0 4px;
}
.timelist a {
background: var(--body-bg);
padding: 4px;
}
.calendar-cancel {
padding: 8px 10px;
}
.clockbox h2 {
padding: 8px 15px;
}
.calendar caption {
padding: 10px;
}
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
z-index: 1;
top: 10px;
}
/* 历史记录 */
/* History */
table#change-history tbody th, table#change-history tbody td {
font-size: 0.8125rem;
word-break: break-word;
}
table#change-history tbody th {
width: auto;
}
/* 文档 */
/* Docs */
table.model tbody th, table.model tbody td {
font-size: 0.8125rem;
word-break: break-word;
}
}

@ -1,109 +0,0 @@
/* 平板设备样式 */
/* TABLETS */
@media (max-width: 1024px) {
/* 从右到左的语言布局调整 */
[dir="rtl"] .colMS {
margin-right: 0;
}
/* 用户工具栏文本对齐调整 */
[dir="rtl"] #user-tools {
text-align: right;
}
/* 变更列表操作标签内边距调整 */
[dir="rtl"] #changelist .actions label {
padding-left: 10px;
padding-right: 0;
}
/* 变更列表操作选择框边距调整 */
[dir="rtl"] #changelist .actions select {
margin-left: 0;
margin-right: 15px;
}
/* 过滤器相关元素的边距调整 */
[dir="rtl"] .change-list .filtered .results,
[dir="rtl"] .change-list .filtered .paginator,
[dir="rtl"] .filtered #toolbar,
[dir="rtl"] .filtered div.xfull,
[dir="rtl"] .filtered .actions,
[dir="rtl"] #changelist-filter {
margin-left: 0;
}
/* 行内组添加行链接的内边距和背景位置调整 */
[dir="rtl"] .inline-group div.add-row a,
[dir="rtl"] .inline-group .tabular tr.add-row td a {
padding: 8px 26px 8px 10px;
background-position: calc(100% - 8px) 9px;
}
/* 对象工具列表项浮动方向调整 */
[dir="rtl"] .object-tools li {
float: right;
}
/* 对象工具相邻列表项的边距调整 */
[dir="rtl"] .object-tools li + li {
margin-left: 0;
margin-right: 15px;
}
/* 仪表板模块表格链接内边距调整 */
[dir="rtl"] .dashboard .module table td a {
padding-left: 0;
padding-right: 16px;
}
}
/* 移动设备样式 */
/* MOBILE */
@media (max-width: 767px) {
/* 相关查找和日期时间快捷方式的边距调整 */
[dir="rtl"] .aligned .related-lookup,
[dir="rtl"] .aligned .datetimeshortcuts {
margin-left: 0;
margin-right: 15px;
}
/* 列表的右边距调整 */
[dir="rtl"] .aligned ul,
[dir="rtl"] form .aligned ul.errorlist {
margin-right: 0;
}
/* 变更列表过滤器的边距调整 */
[dir="rtl"] #changelist-filter {
margin-left: 0;
margin-right: 0;
}
/* 复选框标签的内边距调整 */
[dir="rtl"] .aligned .vCheckboxLabel {
padding: 1px 5px 0 0;
}
/* 选择器移除按钮的背景位置调整 */
[dir="rtl"] .selector-remove {
background-position: 0 0;
}
/* 选择器移除按钮的焦点和悬停状态背景位置调整 */
[dir="rtl"] :enabled.selector-remove:focus, :enabled.selector-remove:hover {
background-position: 0 -24px;
}
/* 选择器添加按钮的背景位置调整 */
[dir="rtl"] .selector-add {
background-position: 0 -48px;
}
/* 选择器添加按钮的焦点和悬停状态背景位置调整 */
[dir="rtl"] :enabled.selector-add:focus, :enabled.selector-add:hover {
background-position: 0 -72px;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save