├── .gitignore
├── LICENSE
├── README.en.md
├── README.md
└── server
├── C.py
├── R.py
├── app.yml
├── dao.py
├── data
└── db
│ └── poster.sqlite
├── fast.py
├── poster.py
├── requirements.txt
├── resource
├── fonts
│ └── 0d44d315557a4a25.woff
└── img
│ └── no-img.jpg
├── static
├── css
│ ├── app.742ff3ff.css
│ └── chunk-vendors.87d3b6c6.css
├── favicon.ico
├── fonts
│ ├── ionicons.143146fa.woff2
│ ├── ionicons.99ac3308.woff
│ └── ionicons.d535a25a.ttf
├── img
│ ├── ionicons.a2c4a261.svg
│ └── no-img.3679ff87.svg
├── index.html
├── js
│ ├── about.01fcc122.js
│ ├── app.32a1d95f.js
│ └── chunk-vendors.7e40b481.js
└── static
│ └── font
│ ├── iconfont.css
│ ├── iconfont.ttf
│ ├── iconfont.woff
│ └── iconfont.woff2
└── store.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | downloads/
14 | eggs/
15 | .eggs/
16 | lib/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | wheels/
22 | share/python-wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .nox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # IPython
77 | profile_default/
78 | ipython_config.py
79 |
80 | # pyenv
81 | .python-version
82 |
83 | # celery beat schedule file
84 | celerybeat-schedule
85 |
86 | # SageMath parsed files
87 | *.sage.py
88 |
89 | # Environments
90 | .env
91 | .venv
92 | env/
93 | venv/
94 | ENV/
95 | env.bak/
96 | venv.bak/
97 |
98 | # Spyder project settings
99 | .spyderproject
100 | .spyproject
101 |
102 | # Rope project settings
103 | .ropeproject
104 |
105 | # mypy
106 | .mypy_cache/
107 | .dmypy.json
108 | dmypy.json
109 |
110 | # Pyre type checker
111 | .pyre/
112 | .DS_Store
113 | .idea
114 | static/storage
115 |
116 | build.sh
117 | cache.sqlite
118 |
119 | #git -c credential.helper= -c core.quotepath=false -c log.showSignature=false fetch github --recurse-submodules=no --progress --prune
120 |
121 | design
122 | *.sh
123 |
124 | server/data/store
125 | .do**
126 | server/static/static/js/
127 | server/static/static/css/
128 |
129 | docker
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 fastposter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.en.md:
--------------------------------------------------------------------------------
1 |

2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | ## Introduction
13 |
14 | Fastposter is a rapid poster development tool that allows you to quickly create posters. Simply upload a background image and place components (`text`, `image`, `QR code`, `avatar`) in the desired positions to generate a poster. Click the `Code` button to directly generate SDK calling code in various languages, making development fast and easy.
15 |
16 | It has served numerous e-commerce projects, with over `80,000` users across multiple projects. Tested in production environments over the years, it's proven to be stable and reliable. It is widely used in various e-commerce, distribution systems, e-commerce posters, e-commerce main images, and other poster generation and production scenarios.
17 |
18 | > If this project has been helpful to you, please give it a star.
19 |
20 | ## Documentation
21 |
22 | - Developer Documentation: [https://fastposter.net/doc/](https://fastposter.net/doc/)
23 | - Java Professional Version - Online Experience: [https://fastposter.net/demo/java/](https://fastposter.net/demo/java/)
24 | - Python Professional Version - Online Experience: [https://fastposter.net/demo/python/](https://fastposter.net/demo/python/)
25 | - Community Version - Online Experience: [https://fastposter.net/demo/open/](https://fastposter.net/demo/open/)
26 | - 🔥🔥Cloud Service - Free Trial: [https://fastposter.net/](https://fastposter.net/)
27 |
28 | ## Features
29 |
30 | - Supports fast Docker deployment.
31 | - Supports production-level e-commerce environments.
32 | - Supports popular SDKs for quick development in `Java`, `Python`, `PHP`, `Go`, `JavaScript`, `mini-program`.
33 | - No need to write complex rendering code.
34 | - Supports multiple file formats: `jpeg`, `png`, `webp`, `pdf`, `base64`.
35 | - Convenient code generation.
36 |
37 |
38 | ## Getting Started
39 |
40 | ### Step 1: Start the Service
41 |
42 | ```bash
43 | docker run -it --name fastposter -p 5000:5000 fastposter/fastposter
44 | ```
45 |
46 | ### Step 2: Edit the Poster
47 |
48 | 
49 |
50 |
51 | ### Step 3: Generate Code
52 |
53 | 
54 |
55 |
56 | Request Example (parameters can be passed directly):
57 |
58 | ```java
59 | // 1. Create a poster client object
60 | FastposterClient client = FastposterClient.builder()
61 | .endpoint("http://127.0.0.1:5000") // Set the access endpoint
62 | .token("ApfrIzxCoK1DwNZOEJCwlrnv6QZ0PCdv") // Set the token
63 | .build();
64 |
65 | // 2. Prepare poster parameters
66 | Map params = new HashMap<>();
67 | params.put("name", "Test Text");
68 |
69 | // 3. Generate and save the poster
70 | client.buildPoster("80058c79d1e2e617").params(params).build().save("demo.png");
71 | ```
72 |
73 |
74 |
75 | ## Use Cases
76 |
77 | - Poster generator
78 | - Automatic poster generation tool
79 | - Online poster design and generation
80 | - Online poster maker
81 | - Generate Moments (WeChat) posters
82 | - E-commerce poster editor
83 | - Certificate creation
84 | - Automatic certificate generation tool
85 | - QR code sharing poster images
86 | - Create posters using Python Pillow
87 | - E-commerce main image editor
88 | - Generate QR code sharing posters using Java
89 | - Create posters with Java Graphics2D
90 | - Generate WeChat mini-program share posters
91 | - Generate QR code posters using PHP
92 | - Custom business poster images
93 | - Generate HTML5 posters
94 | - Create posters using HTML5 Canvas
95 | - Generate posters using JSON data for batch production
96 | - Draw images using BufferedImage
97 |
98 | ## Community
99 |
100 | Author's WeChat: `fastposter`
101 |
102 | 
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | ## 介绍
14 |
15 | fastposter海报生成器是一款快速开发海报的工具。只需上传一张背景图,在对应的位置放上组件(`文字`、`图片`、`二维码`、`头像`)即可生成海报。 点击`代码`直接生成各种语言SDK的调用代码,方便快速开发。
16 |
17 | 现已服务众多电商类项⽬,多个项⽬有`80W+`⽤户,通过多年⽣产环境的考验,稳定可靠。广泛应用于各类电商、分销系统、电商海报、电商主图等海报生成和制作场景。
18 |
19 | > 如果项目有帮到您,请点亮你点亮的小星星
20 |
21 | ## 文档
22 |
23 | - 开发文档:[https://fastposter.net/doc/](https://fastposter.net/doc/)
24 | - Java专业版-在线体验:[https://fastposter.net/demo/java/](https://fastposter.net/demo/java/)
25 | - Python专业版-在线体验:[https://fastposter.net/demo/python/](https://fastposter.net/demo/python/)
26 | - 社区版-在线体验:[https://fastposter.net/demo/open/](https://fastposter.net/demo/open/)
27 | - 🔥🔥云服务-免费试用:[https://fastposter.net/](https://fastposter.net/)
28 |
29 | ## 特性
30 |
31 | - 支持docker快速部署(支持arm和x86架构)
32 | - 支持电商级生产环境
33 | - 主流的SDK支持,方便快速开发 `Java` `Python` `PHP` `Go` `JavaScript` `小程序`
34 | - 无需编写复杂的绘图渲染代码
35 | - 支持多种文件格式 `jpeg` `png` `webp` `pdf` `base64`
36 | - 便捷的代码生成
37 |
38 |
39 | ## 快速开始
40 |
41 | ### 一、启动服务
42 |
43 | ```bash
44 | docker run -it --name fastposter -p 5000:5000 fastposter/fastposter
45 | ```
46 |
47 | ### 二、编辑海报
48 |
49 | 
50 |
51 |
52 | ### 三、生成代码
53 |
54 | Java代码
55 |
56 | 
57 |
58 |
59 | ```java
60 | // 进一步了解,请参考开发文档 https://fastposter.net/doc/sdk/
61 | import net.fastposter.client.FastposterClient;
62 | import java.util.*;
63 |
64 | public class FastposterClientDemo {
65 |
66 | public static void main(String[] args) {
67 |
68 | // 1.创建海报客户端对象
69 | FastposterClient client = FastposterClient.builder()
70 | .endpoint("http://127.0.0.1:5000") // 设置接入端点
71 | .token("ApfrIzxCoK1DwNZOEJCwlrnv6QZ0PCdv") // 设置token
72 | .build();
73 |
74 | // 2.准备海报参数
75 | Map params = new HashMap<>();
76 | params.put("NO", "SN88888888");
77 |
78 |
79 | // 3.生成海报并保存
80 | client.buildPoster("6fba72004fa20aee").params(params).build().save();
81 |
82 | }
83 |
84 | }
85 | ```
86 |
87 | Python 代码
88 |
89 | 
90 |
91 | ```python
92 | # 进一步了解,请参考开发文档 https://fastposter.net/doc/sdk/
93 | from fastposter import Client
94 |
95 | client = Client('ApfrIzxCoK1DwNZOEJCwlrnv6QZ0PCdv', 'http://127.0.0.1:5000')
96 | params = {
97 | "NO": "SN88888888"
98 | }
99 | client.buildPoster('6fba72004fa20aee', params=params).save()
100 | ```
101 |
102 | 响应示例(返回海报图片)
103 |
104 |
105 |
106 |
107 | ## 适用场景
108 |
109 | - 海报生成器
110 | - 海报自动生成工具
111 | - 海报在线设计生成器
112 | - 海报生成器在线制作
113 | - 生成朋友圈海报
114 | - 电商海报编辑器
115 | - 证书制作
116 | - 证书自动生成工具
117 | - 二维码分享海报图片
118 | - Python Pillow绘图 Pillow制作海报
119 | - 电商主图编辑器
120 | - Java生成二维码分享海报图片
121 | - Java Graphics2D绘制海报图片
122 | - 微信小程序生成海报分享朋友圈
123 | - PHP生成二维码海报图片
124 | - 自定义商业海报图片
125 | - H5生成海报图片
126 | - canvas生成海报图片
127 | - 通过JSON生成海报图片
128 | - BufferdImage绘制图片
129 |
130 | ## 社区
131 |
132 | 作者微信`fastposter`
133 |
134 | 
135 |
--------------------------------------------------------------------------------
/server/C.py:
--------------------------------------------------------------------------------
1 | import getopt
2 | import hashlib
3 | import json
4 | import os
5 | import sys
6 | import uuid
7 |
8 | import yaml
9 |
10 | STORE_DB = 'data/db'
11 | STORE_DIR = 'data/store'
12 | STATUS_NORMAL = 1
13 | STATUS_DELETE = 2
14 | TOKEN = None
15 |
16 | config = {}
17 | if os.path.exists('app.yml'):
18 | config = yaml.safe_load(open('app.yml', encoding='utf-8'))
19 |
20 |
21 | def mkdirs(path):
22 | if not os.path.exists(path):
23 | print("mkdirs: path=" + path)
24 | os.makedirs(path)
25 | return path
26 |
27 |
28 | def init_path():
29 | mkdirs(STORE_DB)
30 |
31 |
32 | def md5(param: str, len=32) -> str:
33 | if type(param) != 'str':
34 | param = json.dumps(param)
35 | return hashlib.md5(param.encode()).hexdigest()[0:len]
36 |
37 |
38 | def code(len=16) -> str:
39 | return md5(str(uuid.uuid4()), len)
40 |
41 |
42 | def indocker():
43 | return os.environ.get('FASTPOSTER_IN_DOCKER', None) is not None
44 |
45 |
46 | def init():
47 | global TOKEN
48 | help_info = """fast.py -t """
49 | argv = sys.argv[1:]
50 | token = None
51 | try:
52 | opts, args = getopt.getopt(argv, "ht:", ["token="])
53 | except getopt.GetoptError:
54 | print(help_info)
55 | sys.exit(2)
56 | for opt, arg in opts:
57 | if opt == '-h':
58 | print(help_info)
59 | sys.exit()
60 | elif opt in ("-t", "--token"):
61 | token = arg
62 | if not token:
63 | try:
64 | token = config.get('app')['token']
65 | except Exception:
66 | ...
67 | TOKEN = token
68 | if not token:
69 | print(help_info)
70 | print('TOKEN', TOKEN)
71 |
72 |
73 | def check_token(token):
74 | return TOKEN == token
75 |
76 |
77 | def __load(tk=init()):
78 | ...
79 |
80 |
81 | __load()
82 |
--------------------------------------------------------------------------------
/server/R.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import json
3 |
4 | class DateEncoder(json.JSONEncoder):
5 | def default(self, obj):
6 | if isinstance(obj, datetime.datetime):
7 | return obj.strftime("%Y-%m-%d %H:%M:%S")
8 | else:
9 | return json.JSONEncoder.default(self, obj)
10 |
11 |
12 | class R():
13 | def __init__(self, code=0):
14 | self.d = {'code': code, 'msg': '', 'data': {}}
15 |
16 | def json(self):
17 | return json.dumps(self.d, ensure_ascii=False, cls=DateEncoder)
18 |
19 | def set(self, key, value):
20 | self.d[key] = value
21 | return self
22 |
23 | def add(self, key, value):
24 | self.d['data'][key] = value
25 | return self
26 |
27 | def __str__(self):
28 | return self.json()
29 |
30 |
31 | def ok(msg='success'):
32 | return R().set('msg', msg)
33 |
34 |
35 | def error(msg='failure'):
36 | return R(400).set('msg', msg)
37 |
38 |
39 | def expire(msg='token expired'):
40 | return R(401).set('msg', msg)
--------------------------------------------------------------------------------
/server/app.yml:
--------------------------------------------------------------------------------
1 | app:
2 | name: fastposter
3 | token: ApfrIzxCoK1DwNZOEJCwlrnv6QZ0PCdv
--------------------------------------------------------------------------------
/server/dao.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import json
3 | import sqlite3
4 |
5 | import C
6 | import poster
7 | import R
8 | import store
9 |
10 |
11 | def conn():
12 | return sqlite3.connect(C.STORE_DB + '/poster.sqlite')
13 |
14 |
15 | def table(sql):
16 | name = sql.split(' ')[2].strip()
17 | with conn() as con:
18 | c = con.cursor()
19 | r = c.execute("select count(1) from sqlite_master where tbl_name = ?", [name])
20 | if r.fetchone()[0] == 0:
21 | c.execute(sql)
22 | print(f"{name} created successfully.")
23 |
24 |
25 | INIT_SQL = [
26 | 'CREATE TABLE posters (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, code text, name text, preview text, json text, create_time date, update_time date, status integer)',
27 | 'CREATE TABLE links (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, code text, pid integer, params text, create_time date)',
28 | ]
29 |
30 |
31 | def init():
32 | C.init_path()
33 | for sql in INIT_SQL: table(sql)
34 |
35 |
36 | init()
37 |
38 |
39 | def db_save_poster(code: str, name: str, preview: str, json: str):
40 | with conn() as con:
41 | c = con.cursor()
42 | params = [code, name, preview, json, now_str(), int(C.STATUS_NORMAL)]
43 | c.execute("insert into posters (code, name, preview, json, create_time, status) values (?, ?, ?, ?, ?, ?)",
44 | params)
45 | con.commit()
46 | return c.lastrowid
47 |
48 |
49 | def db_update_poster(id: int, code: str, name: str, preview: str, json: str):
50 | with conn() as con:
51 | c = con.cursor()
52 | params = [name, preview, json, now_str(), id]
53 | c.execute("update posters set name=?,preview=?,json=?,update_time=? where id=?", params)
54 | con.commit()
55 |
56 |
57 | def db_delete_poster(id: int):
58 | with conn() as con:
59 | c = con.cursor()
60 | params = [C.STATUS_DELETE, id]
61 | c.execute("update posters set status=? where id=?", params)
62 | con.commit()
63 |
64 |
65 | def db_save_share(code, poster_id, param):
66 | with conn() as con:
67 | c = con.cursor()
68 | params = [code, poster_id, param, now_str()]
69 | c.execute("insert into links (code, pid, params, create_time) values (?, ?, ?, ?)", params)
70 | con.commit()
71 | return c.lastrowid
72 |
73 |
74 | def query_user_posters():
75 | with conn() as con:
76 | c = con.cursor()
77 | r = c.execute('select * from posters where status=1 order by id desc')
78 | posters = []
79 | for row in r:
80 | posters.append({
81 | 'id': row[0],
82 | 'code': row[1],
83 | 'name': row[2],
84 | 'preview': row[3],
85 | 'json': row[4],
86 | 'createTime': row[5],
87 | 'updateTime': row[6],
88 | # 'status': row[7],
89 | })
90 | return posters
91 |
92 |
93 | def query_user_poster(poster_id=0, uuid=None):
94 | with conn() as con:
95 | c = con.cursor()
96 | if uuid:
97 | r = c.execute('select * from posters where code = ? limit 1', [uuid])
98 | else:
99 | r = c.execute('select * from posters where id = ? limit 1', [poster_id])
100 | row = r.fetchone()
101 | if row is not None:
102 | return {
103 | 'id': row[0],
104 | 'code': row[1],
105 | 'name': row[2],
106 | 'preview': row[3],
107 | 'json': row[4],
108 | 'createTime': row[5],
109 | 'updateTime': row[6],
110 | 'status': row[7],
111 | }
112 | else:
113 | return None
114 |
115 |
116 | def query_user_share(code: str):
117 | with conn() as con:
118 | c = con.cursor()
119 | r = c.execute('select * from links where code = ? limit 1', [code])
120 | row = r.fetchone()
121 | if row is not None:
122 | return {
123 | 'id': row[0],
124 | 'code': row[1],
125 | 'pid': row[2],
126 | 'params': row[3],
127 | 'createTime': row[4],
128 | }
129 | else:
130 | return None
131 |
132 |
133 | def now_str(days=0):
134 | return (datetime.datetime.now() + datetime.timedelta(days=days)).strftime('%Y-%m-%d %H:%M:%S')
135 |
136 |
137 | def save_user_poster(data, pd):
138 | code = C.code()
139 | name = data['name']
140 | buf, mimetype = poster.drawio(pd, 0.4)
141 | path = store.save(buf.getvalue(), f"a.{pd['type']}", 'preview')
142 | return db_save_poster(code, name, path, data['json'])
143 |
144 |
145 | def update_user_poster(data, pd, id):
146 | code = data.get('code', C.code())
147 | name = data['name']
148 | buf, mimetype = poster.drawio(pd, 0.4)
149 | path = store.save(buf.getvalue(), f"a.{pd['type']}", 'preview')
150 | return db_update_poster(id, code, name, path, data['json'])
151 |
152 |
153 | def save_or_update_user_poster(data):
154 | pd = json.loads(data['json'])
155 | print(pd)
156 | id = data.get('id', 0)
157 | if id == 0:
158 | return save_user_poster(data, pd)
159 | else:
160 | return update_user_poster(data, pd, id)
161 |
162 |
163 | def copy_user_poster(id):
164 | p = query_user_poster(id)
165 | if p:
166 | code = C.code()
167 | return db_save_poster(code, p['name'] + '-复制', p['preview'], p['json'])
168 | return None
169 |
170 |
171 | def get_share_link(code, param):
172 | s = query_user_share(code)
173 | if s:
174 | return True
175 | if param.get('id', None):
176 | posterId = int(param['id'])
177 | elif param.get('posterId', None):
178 | posterId = int(param['posterId'])
179 | p = query_user_poster(posterId)
180 | if p is None:
181 | print('海报不存在')
182 | return R.error('海报不存在').json()
183 | db_save_share(code, posterId, json.dumps(param))
184 | return True
185 |
186 |
187 | def find_share_data(code):
188 | s = query_user_share(code)
189 | if s is None:
190 | return None
191 | param = json.loads(s['params'])
192 | if param.get('id', None):
193 | posterId = int(param['id'])
194 | elif param.get('posterId', None):
195 | posterId = int(param['posterId'])
196 | p = query_user_poster(posterId)
197 |
198 | return merge_params(p, param)
199 |
200 |
201 | def find_build_data(uuid, param):
202 | p = query_user_poster(uuid=uuid)
203 | if p is None:
204 | return None
205 | return merge_params(p, param)
206 |
207 |
208 | def merge_params(p, param):
209 | d = json.loads(p['json'])
210 | for item in d['items']:
211 | vd = item['vd'].strip()
212 | if vd:
213 | if not param.get(vd, None):
214 | continue
215 | v = param[vd]
216 | if v:
217 | item['v'] = v.strip()
218 | # 处理背景图片
219 | if param.get('bgUrl', None):
220 | d['bgUrl'] = param['bgUrl']
221 | return d
222 |
--------------------------------------------------------------------------------
/server/data/db/poster.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psoho/fast-poster/9f5fd7705c4b2107d4a9ff880d042c217c6ab757/server/data/db/poster.sqlite
--------------------------------------------------------------------------------
/server/fast.py:
--------------------------------------------------------------------------------
1 | import base64
2 | import os
3 | import re
4 | from os.path import join, dirname
5 |
6 | import tornado.ioloop
7 | from tornado.web import RequestHandler, StaticFileHandler, Application
8 |
9 | import C
10 | import R
11 | import dao
12 | import json
13 | import poster
14 | import store
15 |
16 |
17 | class BaseHandler(RequestHandler):
18 |
19 | def set_default_headers(self) -> None:
20 | origin_url = self.request.headers.get('Origin')
21 | if not origin_url: origin_url = '*'
22 | self.set_header('Access-Control-Allow-Methods', 'POST, PUT, DELETE, GET, OPTIONS')
23 | self.set_header('fastposter', 'fastposter/v2.19.1')
24 | self.set_header('Access-Control-Allow-Credentials', 'true')
25 | self.set_header('Access-Control-Allow-Origin', origin_url)
26 | self.set_header('Access-Control-Allow-Headers', 'x-requested-with,token,Content-type,Client-Type,Client-Version')
27 |
28 | def options(self):
29 | self.set_status(200)
30 | self.finish()
31 |
32 | def json(self, r: R):
33 | self.set_header('Content-Type', 'application/json;charset=UTF-8')
34 | self.write(r.json())
35 |
36 |
37 | class BaseAuthHandler(BaseHandler):
38 |
39 | def prepare(self):
40 | self.check_token()
41 |
42 | def token(self):
43 | t = self.request.headers['token'] if 'token' in self.request.headers else None
44 | if not t:
45 | t = self.get_argument('token', None)
46 | if not t:
47 | t = self.get_body_argument('token', None)
48 | return t
49 |
50 | def check_token(self):
51 | t = self.token()
52 | if not t:
53 | self.json(R.expire('not token'))
54 | return self.finish()
55 | if not C.check_token(t):
56 | self.json(R.expire())
57 | return self.finish()
58 |
59 |
60 | class ApiLoginHandler(BaseHandler):
61 |
62 | def post(self):
63 | token = self.get_body_argument('token')
64 | if C.check_token(token):
65 | self.json(R.ok('login success.').add('token', token))
66 | else:
67 | self.json(R.error('token not match!'))
68 |
69 |
70 | class ApiUserInfoHandler(BaseAuthHandler):
71 |
72 | def get(self):
73 | self.json(R.ok().add('user', {"id":2,"username":"user1","type":1,"status":1}))
74 |
75 |
76 | class ApiPostersHandler(BaseAuthHandler):
77 |
78 | def get(self, id):
79 | poster = dao.query_user_poster(id)
80 | self.write(R.ok().add('poster', poster).json())
81 |
82 |
83 | class ApiUserPostersHandler(BaseAuthHandler):
84 |
85 | def get(self):
86 | posters = dao.query_user_posters()
87 | self.write(R.ok().add('posters', posters).json())
88 |
89 | def delete(self, id):
90 | dao.db_delete_poster(int(id))
91 | self.write(R.ok().json())
92 |
93 | def post(self):
94 | data = json.loads(self.request.body)
95 | id = dao.save_or_update_user_poster(data)
96 | self.write(R.ok().add("id", id).json())
97 |
98 |
99 | class ApiUserPostersCopyHandler(BaseAuthHandler):
100 |
101 | def post(self, id):
102 | id = dao.copy_user_poster(id)
103 | self.json(R.ok().add("id", id))
104 |
105 |
106 | class ApiPreviewHandler(BaseAuthHandler):
107 |
108 | def post(self):
109 | data = json.loads(self.request.body)
110 | buf, mimetype = poster.drawio(data)
111 | self.set_header('Content-Type', mimetype)
112 | self.write(buf.getvalue())
113 |
114 |
115 | class ApiUploadHandler(BaseAuthHandler):
116 |
117 | def post(self):
118 | for field_name, fs in self.request.files.items():
119 | for f in fs:
120 | filename, body, content_type = f["filename"], f['body'], f["content_type"]
121 | path = store.save(body, filename)
122 | break
123 | self.json(R.ok().add("url", path))
124 |
125 |
126 | class ApiLinkHandler(BaseAuthHandler):
127 |
128 | def post(self):
129 | param = json.loads(self.request.body)
130 | code = C.md5(param, 16)
131 | if dao.get_share_link(code, param):
132 | url = f"{uri}/v/{code}".replace('//v', '/v')
133 | self.json(R.ok().add('url', url))
134 | else:
135 | self.json(R.error(f'the poster [{param["posterId"]}] not exits.'))
136 |
137 |
138 | class ApiBuildPosterHandler(BaseAuthHandler):
139 |
140 | def post(self):
141 | args = json.loads(self.request.body)
142 | print(args)
143 | traceId = C.code(32)
144 | payload = args['payload'] # type: str
145 | if not payload.startswith("{"):
146 | # 需要base64解码
147 | payload = base64.b64decode(payload)
148 | data = dao.find_build_data(args['uuid'], json.loads(payload))
149 | if data is None:
150 | print('no poster here!')
151 | self.write(R.error('no poster here!'))
152 | return
153 | buf, mimetype = poster.drawio(data)
154 | self.set_header('fastposter-traceid', traceId)
155 | if args.get('b64', False):
156 | b64 = base64.b64encode(buf.read()).decode()
157 | self.write(b64)
158 | else:
159 | self.set_header('Content-Type', mimetype)
160 | self.write(buf.getvalue())
161 |
162 |
163 | class ApiViewHandler(BaseHandler):
164 |
165 | def get(self, code: str):
166 | c = code.split('.')
167 | data = dao.find_share_data(c[0])
168 | if data is None:
169 | print('no poster here!')
170 | self.write(R.error('no poster here!'))
171 | return
172 | if len(c) == 2 and (c[1] == 'png'):
173 | data['type'] = c[1]
174 | buf, mimetype = poster.drawio(data)
175 | if len(c) == 2 and c[1].startswith('b64'):
176 | b64 = base64.b64encode(buf.read()).decode()
177 | self.write(b64)
178 | else:
179 | self.set_header('Content-Type', mimetype)
180 | self.write(buf.getvalue())
181 |
182 |
183 | class MyStaticFileHandler(StaticFileHandler, BaseHandler):
184 | pass
185 |
186 |
187 | def make_app(p):
188 | # path = "static" if C.indocker() else "../design/dist"
189 | path = "static"
190 | settings = {
191 | 'debug': not C.indocker() or os.environ.get('POSTER_DEBUG', 'false') == 'true'
192 | }
193 | print('p', p)
194 | return Application([
195 | (f"{p}api/user/info", ApiUserInfoHandler),
196 | (f"{p}api/login", ApiLoginHandler),
197 | (f"{p}api/user/posters", ApiUserPostersHandler),
198 | (f"{p}api/user/posters/copy/(.+)", ApiUserPostersCopyHandler),
199 | (f"{p}api/user/posters/(.+)", ApiUserPostersHandler),
200 | (f"{p}api/user/poster/(.+)", ApiPostersHandler),
201 | (f"{p}api/preview", ApiPreviewHandler),
202 | (f"{p}api/upload", ApiUploadHandler),
203 | (f"{p}api/link", ApiLinkHandler),
204 | (f"{p}v1/build/poster", ApiBuildPosterHandler),
205 | (f"{p}v/(.+)", ApiViewHandler),
206 | (f'{p}(store/.*)$', StaticFileHandler, {"path": join(dirname(__file__), "data")}),
207 | (f'{p}resource/(.*)$', MyStaticFileHandler, {"path": join(dirname(__file__), "resource")}),
208 | (f'{p}(.*)$', StaticFileHandler, {"path": join(dirname(__file__), path), "default_filename": "index.html"})
209 |
210 | ], **settings)
211 |
212 |
213 | if __name__ == "__main__":
214 | banner = '''
215 | __ _ _
216 | / _| | | | |
217 | | |_ __ _ ___ | |_ _ __ ___ ___ | |_ ___ _ __
218 | | _| / _` |/ __|| __|| '_ \ / _ \ / __|| __| / _ \| '__|
219 | | | | (_| |\__ \| |_ | |_) || (_) |\__ \| |_ | __/| |
220 | |_| \__,_||___/ \__|| .__/ \___/ |___/ \__| \___||_|
221 | | |
222 | |_|
223 | fastposter(v2.19.1)
224 | https://fastposter.net/doc/
225 | '''
226 | PORT = 5000
227 | print(banner)
228 | uri = os.environ.get('POSTER_URI_PREFIX', f'http://0.0.0.0:{PORT}/')
229 | print(f'Listening at {uri}\n')
230 | g = re.search(r'http[s]?://.*?(/.*)', uri)
231 | web_context_path = '/' if not g else g.group(1)
232 | app = make_app(web_context_path)
233 | app.listen(port=PORT, address='0.0.0.0')
234 | tornado.ioloop.IOLoop.current().start()
235 |
--------------------------------------------------------------------------------
/server/poster.py:
--------------------------------------------------------------------------------
1 | import os
2 | import traceback
3 | from io import BytesIO
4 |
5 | import qrcode
6 | import requests
7 | import requests_cache
8 | import urllib3
9 | from PIL import Image, ImageDraw, ImageFont
10 |
11 | import C
12 |
13 | headers = {
14 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36'
15 | }
16 |
17 | NO_IMG = Image.open(os.path.join(os.path.dirname(__file__), 'resource/img/no-img.jpg')).convert('RGBA')
18 | requests_cache.install_cache(C.STORE_DB + '/cache')
19 |
20 |
21 | def fetchImg(url=''):
22 | try:
23 | if url.startswith('store/upload/'):
24 | if os.path.exists(f'data/{url}'):
25 | return Image.open(f'data/{url}').convert('RGBA')
26 | else:
27 | return NO_IMG
28 | r = requests.get(url, timeout=0.2)
29 | return Image.open(BytesIO(r.content)).convert('RGBA')
30 | except urllib3.exceptions.ReadTimeoutError:
31 | print(f'ERROR: fetch image timeout: url={url}')
32 | traceback.print_exc()
33 | return None
34 | except Exception:
35 | traceback.print_exc()
36 | return NO_IMG
37 |
38 |
39 | def drawImg(draw, d, bg):
40 | url, w, h, x, y = d['v'], d['w'], d['h'], d['x'], d['y']
41 | try:
42 | img = fetchImg(url)
43 | if img == None:
44 | return
45 | img = img.resize((w, h), Image.ANTIALIAS)
46 | bg.paste(img, (x, y), img)
47 | except Exception as e:
48 | print('绘制图片异常: %s' % e)
49 | pass
50 |
51 |
52 | def drawBg(item):
53 | url, w, h, c = str(item['bgUrl']), item['w'], item['h'], item['bgc']
54 | c = '#fafbfc00' if c == '' else c
55 | if not url.strip():
56 | img = Image.new('RGBA', (w, h), c)
57 | else:
58 | img = fetchImg(url)
59 | img = img.resize((w, h), Image.ANTIALIAS)
60 | draw = ImageDraw.Draw(img)
61 | return img, draw
62 |
63 |
64 | def getFont(item):
65 | fn, size = item['fn'], item['s']
66 | if fn == "":
67 | fn = '0d44d315557a4a25.woff'
68 | font = 'resource/fonts/' + fn
69 | if not os.path.exists(font):
70 | font = 'resource/fonts/0d44d315557a4a25.woff'
71 | return ImageFont.truetype(font, size)
72 |
73 |
74 | def wrap_text(text, font, width):
75 | sb = []
76 | temp = ''
77 | for s in text:
78 | if s == '\n': # 优化本文中含有换行符
79 | sb.append(temp)
80 | temp = ''
81 | continue
82 | t = temp + s
83 | if font.getsize(t)[0] > width:
84 | sb.append(temp)
85 | temp = s
86 | else:
87 | temp += s
88 | if temp != '':
89 | sb.append(temp)
90 | return sb
91 |
92 |
93 | def drawText(draw, item, bg):
94 | font = getFont(item)
95 | v, w, h, x, y, c = item['v'], item['w'], item['h'], item['x'], item['y'], item.get('c', '#010203')
96 | img = Image.new("RGBA", (w, h), '#fff0')
97 | draw = ImageDraw.Draw(img) # type:ImageDraw.ImageDraw
98 | t = wrap_text(v, font, w)
99 | draw.text((0, 0), '\n'.join(t), fill=c, font=font)
100 | if img is not None:
101 | bg.paste(img, (x, y), img)
102 |
103 |
104 | def drawQrCode(draw, item, bg):
105 | url, w, h, x, y, c = item['v'], item['w'], item['h'], item['x'], item['y'], item.get('c', '#010203').strip()
106 | c = '#010203' if len(c) == 0 else c
107 | p = item.get('p', 0)
108 | qr = qrcode.QRCode(
109 | version=2,
110 | error_correction=qrcode.constants.ERROR_CORRECT_M,
111 | box_size=10,
112 | border=p,
113 | )
114 | qr.add_data(url)
115 | qr.make(fit=True)
116 | img = qr.make_image(fill_color=c, back_color="#ffffff")
117 | img = img.resize((w, h), Image.ANTIALIAS)
118 | bg.paste(img, (x, y), None)
119 |
120 |
121 | def drawAvatar(draw, item, bg):
122 | url, w, h, x, y, c = item['v'], item['w'], item['h'], item['x'], item['y'], item.get('c', '#ffffff').strip()
123 | c = '#ffffff' if len(c) == 0 else c
124 | im = fetchImg(url)
125 | if im == None:
126 | return
127 | bigsize = (im.size[0] * 3, im.size[1] * 3)
128 | mask = Image.new('L', bigsize, 0)
129 | draw = ImageDraw.Draw(mask)
130 | draw.ellipse((0, 0) + bigsize, fill=255)
131 | mask = mask.resize(im.size, Image.ANTIALIAS)
132 | im.putalpha(mask)
133 | im = im.resize((w, h), Image.ANTIALIAS)
134 | mask = Image.new('RGBA', bigsize)
135 | draw = ImageDraw.Draw(mask)
136 | draw.ellipse((0, 0) + bigsize, outline=c, width=4 * 3)
137 | mask = mask.resize(im.size, Image.ANTIALIAS)
138 | im.paste(mask, (0, 0), mask)
139 | bg.paste(im, (x, y), im)
140 | pass
141 |
142 |
143 | def draw(data):
144 | img, draw = drawBg(data)
145 |
146 | for item in data['items']:
147 | type = item['t']
148 | if 'text' == type:
149 | drawText(draw, item, bg=img)
150 | if 'image' == type:
151 | drawImg(draw, item, bg=img)
152 | if 'avatar' == type:
153 | drawAvatar(draw, item, bg=img)
154 | if 'qrcode' == type:
155 | url = item.get('v', '')
156 | if url.startswith('img:'):
157 | url = url[4:]
158 | item['v'] = url
159 | drawImg(draw, item, bg=img)
160 | else:
161 | drawQrCode(draw, item, bg=img)
162 |
163 | if data['type'] == "jpeg":
164 | img = img.convert("RGB")
165 | return img
166 |
167 |
168 | def drawio(data, scale=1):
169 | type = data['type']
170 | if type == "jpg":
171 | type = "jpeg"
172 | data['type'] = type
173 | mimetype = "image/" + data['type']
174 | img = draw(data)
175 | quality = data['quality']
176 | if scale < 1:
177 | w = img.size[0]
178 | h = img.size[1]
179 | img = img.resize((int(w * scale), int(h * scale)), Image.ANTIALIAS)
180 | buf = BytesIO()
181 | img.save(buf, type, quality=quality, progressive=True)
182 | buf.seek(0)
183 | return buf, mimetype
184 |
--------------------------------------------------------------------------------
/server/requirements.txt:
--------------------------------------------------------------------------------
1 | requests==2.31.0
2 | Pillow==9.5.0
3 | qrcode==7.4.2
4 | requests_cache==1.1.0
5 | tornado==6.2
6 | pyyaml==6.0.1
--------------------------------------------------------------------------------
/server/resource/fonts/0d44d315557a4a25.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psoho/fast-poster/9f5fd7705c4b2107d4a9ff880d042c217c6ab757/server/resource/fonts/0d44d315557a4a25.woff
--------------------------------------------------------------------------------
/server/resource/img/no-img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psoho/fast-poster/9f5fd7705c4b2107d4a9ff880d042c217c6ab757/server/resource/img/no-img.jpg
--------------------------------------------------------------------------------
/server/static/css/app.742ff3ff.css:
--------------------------------------------------------------------------------
1 | #app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;color:#2c3e50;width:100%;height:100%;position:absolute}div.ivu-modal-content{border-radius:0}div.ivu-modal-footer{display:none}div.ivu-modal{top:132px}button.ivu-btn{border-radius:0}.ivu-modal-wrap .code *{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}.poster-item-qrcode[data-v-0a24c513]{width:100%!important;height:100%!important}.item-image[data-v-e03238ea]{width:100%;height:100%}.poster-item .vdr.active:before{content:"";width:100%;height:100%;top:0;left:0;outline:1px solid #6ccfff}.vdr{text-align:left}.vdr.active{cursor:move}.poster-item-vue-drag :hover{outline:1px solid #6ccfff}.poster-item-vue-drag .vdr-stick-bl,.poster-item-vue-drag .vdr-stick-br,.poster-item-vue-drag .vdr-stick-tl,.poster-item-vue-drag .vdr-stick-tr{border-radius:50%}.poster-item-vue-drag .vdr-stick{width:12px!important;height:12px!important;border:1px solid rgba(0,0,0,.2)}.poster-item-vue-drag .vdr-stick:hover{outline:none}.poster-item-vue-drag .vdr-stick-ml,.poster-item-vue-drag .vdr-stick-mr{width:7px!important;height:14px!important;border-radius:6px}.poster-item-vue-drag .vdr-stick-bm,.poster-item-vue-drag .vdr-stick-tm{width:14px!important;height:7px!important;border-radius:6px}.poster-item-vue-drag .vdr-stick-tl{top:-6px!important;left:-6px!important}.poster-item-vue-drag .vdr-stick-tr{top:-6px!important;right:-6px!important}.poster-item-vue-drag .vdr-stick-bl{bottom:-6px!important;left:-6px!important}.poster-item-vue-drag .vdr-stick-br{bottom:-6px!important;right:-6px!important}.poster-item-vue-drag .vdr-stick-ml,.poster-item-vue-drag .vdr-stick-mr{margin-top:-7px!important}.poster-item-vue-drag .vdr-stick-bm,.poster-item-vue-drag .vdr-stick-tm{margin-left:-6px!important}.contextmenu[data-v-014fdc05]{position:absolute;z-index:1000}.contextmenu ul[data-v-014fdc05]{border:1px solid #e4e7ed;border-radius:4px;background-color:#fff;-webkit-box-shadow:0 2px 12px 0 rgba(0,0,0,.1);box-shadow:0 2px 12px 0 rgba(0,0,0,.1);-webkit-box-sizing:border-box;box-sizing:border-box;margin:5px 0;padding:6px 0;text-align:left}.contextmenu ul li[data-v-014fdc05]{font-size:14px;padding:0 20px;position:relative;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#606266;height:34px;line-height:34px;-webkit-box-sizing:border-box;box-sizing:border-box;cursor:pointer}.contextmenu ul li[data-v-014fdc05]:hover{background-color:#f5f7fa}p[data-v-2a577a8e]{margin:0;padding:0}.content-warpper[data-v-2a577a8e]{text-align:center;width:100%;height:calc(100vh - 58px);background-color:#f1f3f7}.content-warpper .canvas-wrapper[data-v-2a577a8e]{position:absolute;left:80px;right:260px;top:0;bottom:0;background:#f1f3f7;overflow:auto;-webkit-transition:left .3s ease-in-out;transition:left .3s ease-in-out}.content-warpper .canvas-wrapper.sidebar-extend[data-v-2a577a8e]{left:408px}.content-warpper .canvas-content-wrapper[data-v-2a577a8e]{background-color:#fff;position:relative;display:flow-root;-webkit-box-shadow:rgba(0,0,0,.2) 1px 1px 15px;box-shadow:1px 1px 15px rgba(0,0,0,.2);margin:80px auto}.content-warpper .canvas-content-wrapper .canvas-content[data-v-2a577a8e]{padding:0;margin:0;height:100%}.content-warpper .canvas-content-wrapper .canvas-content.bl[data-v-2a577a8e],.content-warpper .canvas-content-wrapper .canvas-content.br[data-v-2a577a8e]{border-right:1px dashed var(--primary)}.content-warpper .canvas-content-wrapper .canvas-content.bt[data-v-2a577a8e]{border-top:1px dashed var(--primary)}.content-warpper .canvas-content-wrapper .canvas-content.bb[data-v-2a577a8e]{border-bottom:1px dashed var(--primary)}.content-warpper .canvas-tool-bar[data-v-2a577a8e]{width:120px;margin-left:-100px;height:36px;background:#fff;border-radius:4px;position:fixed;right:300px;bottom:25px;color:#333;line-height:36px;font-size:12px;z-index:99;-webkit-box-shadow:0 2px 8px 0 rgba(0,0,0,.06);box-shadow:0 2px 8px 0 rgba(0,0,0,.06)}.content-warpper .canvas-tool-bar .scale-area[data-v-2a577a8e]{margin:0 auto;width:88px;font-size:14px;font-weight:700}.content-warpper .canvas-tool-bar .scale-area .icon-minus[data-v-2a577a8e]{float:left;font-size:20px;cursor:pointer}.content-warpper .canvas-tool-bar .scale-area .scale-num[data-v-2a577a8e]{display:inline-block;font-weight:200}.content-warpper .canvas-tool-bar .scale-area .icon-plus[data-v-2a577a8e]{float:right;font-size:20px;cursor:pointer}.content-warpper .canvas-tool-bar .iconfont[data-v-2a577a8e]{float:left;font-size:14px;cursor:pointer}.content-warpper .canvas-tool-bar .iconfont[data-v-2a577a8e]:hover{color:#555}.my-poster-list[data-v-577ecc48]{padding-top:30px;overflow-y:auto}.my-poster-list .picture-container[data-v-577ecc48]{padding:0 16px 20px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-ms-flex-wrap:wrap;flex-wrap:wrap}.my-poster-list .my-poster-item[data-v-577ecc48]{width:130px;height:230px;border:1px solid #e0e5ea;border-radius:4px;overflow:hidden;position:relative;margin:4px}.my-poster-list .my-poster-item .item-title[data-v-577ecc48]{position:absolute;display:none;width:100%;height:100%}.my-poster-list .my-poster-item .item-title .title[data-v-577ecc48]{width:100%;cursor:auto;color:#fff;background:#2c3e50;opacity:.8;font-size:11px;display:inline-block;border-radius:2px;padding:4px}.my-poster-list .my-poster-item .item-title .btn-delete[data-v-577ecc48]{position:absolute;display:block;bottom:0;right:2px;cursor:pointer}.my-poster-list .my-poster-item .item-title .btn-delete[data-v-577ecc48]:hover{color:#ff787b;font-size:20px;font-weight:bolder;-webkit-transition:font-size .3s ease-in-out;transition:font-size .3s ease-in-out}.my-poster-list .my-poster-item .item-title .btn-copy[data-v-577ecc48]{position:absolute;display:block;bottom:0;left:2px;cursor:pointer}.my-poster-list .my-poster-item .item-title .btn-copy[data-v-577ecc48]:hover{color:var(--primary);font-size:20px;font-weight:bolder;-webkit-transition:font-size .3s ease-in-out;transition:font-size .3s ease-in-out}.my-poster-list .my-poster-item:hover .item-title[data-v-577ecc48]{display:block}.my-poster-list .my-poster-item[data-v-577ecc48]:before{content:" ";background:rgba(0,0,0,.15);position:absolute;left:0;top:0;right:0;bottom:0;opacity:0;-webkit-transition:opacity .2s ease;transition:opacity .2s ease}.my-poster-list .my-poster-item[data-v-577ecc48]:hover:before{opacity:1}.my-poster-list .poster-preview-img[data-v-577ecc48]{width:130px;height:230px;min-height:100px}.setting-layer[data-v-26ff44a0]{padding:6px;color:var(--primary);color:var(--text);background-color:#f1f3f7;border-radius:2px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;margin:6px 4px 0;font-size:12px}.setting-layer .mr8[data-v-26ff44a0]{margin-right:10px}.setting-layer .warn[data-v-26ff44a0]{color:var(--red)}.setting-layer .icon[data-v-26ff44a0]{font-size:16px}.setting-layer.active[data-v-26ff44a0]{color:var(--primary)}.setting-layer[data-v-26ff44a0]:focus{outline:1px dashed var(--text);-webkit-transition:opacity 1s ease-in-out;transition:opacity 1s ease-in-out;-webkit-animation:glow-data-v-26ff44a0 .8s ease-out infinite alternate;animation:glow-data-v-26ff44a0 .8s ease-out infinite alternate}@-webkit-keyframes glow-data-v-26ff44a0{0%{outline:1px dashed #ccc;-webkit-box-shadow:none;box-shadow:none}to{outline:1px dashed var(--primary);-webkit-box-shadow:rgba(0,0,0,.3) 1px 1px 6px;box-shadow:1px 1px 6px rgba(0,0,0,.3)}}@keyframes glow-data-v-26ff44a0{0%{outline:1px dashed #ccc;-webkit-box-shadow:none;box-shadow:none}to{outline:1px dashed var(--primary);-webkit-box-shadow:rgba(0,0,0,.3) 1px 1px 6px;box-shadow:1px 1px 6px rgba(0,0,0,.3)}}.tool-layout[data-v-9d9eaeae]{position:absolute;height:100%}.tool-layout .preview-picture[data-v-9d9eaeae]{display:none}.nav-layout[data-v-9d9eaeae]{position:absolute;left:0;top:0;width:80px;background-color:#fff;border-right:1px solid #f1f3f7;height:100%;padding-top:10px;z-index:12}.nav-layout .nav-item[data-v-9d9eaeae]{position:relative;display:inline-block;text-align:center;width:100%;height:66px;cursor:pointer;margin-top:1px;border-left:4px solid #fff;border-right:4px solid #fff}.nav-layout .nav-item .nav-item-text[data-v-9d9eaeae]{color:var(--text);font-size:12px;margin-top:4px}.nav-layout .nav-item.active[data-v-9d9eaeae]{color:var(--primary);border-left-color:var(--primary)}.nav-layout .nav-item.active .iconfont[data-v-9d9eaeae],.nav-layout .nav-item.active .nav-item-text[data-v-9d9eaeae]{color:var(--primary)}.nav-layout .nav-item[data-v-9d9eaeae]:hover{border-left-color:var(--primary)}.nav-layout .nav-item .iconfont[data-v-9d9eaeae]{display:inline-block;margin-top:4px;font-size:24px;color:var(--text)}.tool-extend-wrapper[data-v-9d9eaeae]{position:absolute;top:0;height:100%;width:328px;background:#fff;margin-left:80px;left:-328px;z-index:11;-webkit-transition:left .3s ease-in-out;transition:left .3s ease-in-out}.tool-extend-wrapper[data-v-9d9eaeae]:hover{outline:none}.tool-extend-wrapper.active[data-v-9d9eaeae]{left:0}.tool-extend-wrapper .panel-layers[data-v-9d9eaeae]{width:100%;height:100%;text-align:center;overflow:hidden}.tool-extend-wrapper .panel-layers .panel-layers-title[data-v-9d9eaeae]{padding:10px;background-color:#fff}.tool-extend-wrapper .panel-layers .panel-layers-items[data-v-9d9eaeae]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:reverse;-ms-flex-direction:column-reverse;flex-direction:column-reverse;text-align:left;background-color:#fff;padding:12px;overflow:auto}.setting-item-base{text-align:center;vertical-align:center}.setting-item-base .input{width:56px}.setting-item-base .ivu-input-number-handler-wrap{width:18px!important}.setting-item-base .ivu-row{margin-top:6px}.setting-item-base .lh32{line-height:32px}.setting-item-base .lh36{line-height:36px}.setting-item-base .mt10{margin-top:10px}.property-layout{text-align:center;vertical-align:center}.property-layout .input{width:56px}.property-layout .ivu-input-number-handler-wrap{width:18px!important}.property-layout .ivu-row{margin-top:6px}.property-layout .lh32{line-height:32px}.property-layout .lh36{line-height:36px}.property-layout .mt10{margin-top:10px}.property-layout .mt20{margin-top:20px}p[data-v-16c46d15]{margin:0;padding:0}.property-layout[data-v-16c46d15]{position:absolute;right:0;top:0;bottom:0;background:#fff;width:260px;-webkit-box-shadow:0 0 10px #cecece;box-shadow:0 0 10px #cecece;z-index:99}.property-layout .title[data-v-16c46d15]{width:100%;height:59px;border-bottom:1px solid #e4e9ee}.property-layout .title .item[data-v-16c46d15]{width:130px;height:60px;text-align:center;float:left;cursor:pointer}.property-layout .title .item p[data-v-16c46d15]{padding:0;margin:0;height:58px;line-height:58px;font-size:16px;color:var(--text);display:inline-block}.property-layout .title .item.active p[data-v-16c46d15],.property-layout .title .item:hover p[data-v-16c46d15]{border-bottom:2px solid var(--primary)}.property-layout .canvas-setting[data-v-16c46d15]{width:100%;padding-top:20px;height:calc(100% - 80px)}.property-layout .canvas-setting .panel-item[data-v-16c46d15]{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:justify;-webkit-box-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-left:19px;margin-bottom:10px}.property-layout .canvas-setting .panel-item .item-title[data-v-16c46d15]{display:inline;font-size:14px;color:#666;float:left}.property-layout .canvas-setting .panel-item .input-area[data-v-16c46d15]{right:20px;position:relative}.property-layout .canvas-setting .panel-item .input-area input[data-v-16c46d15]{background:#f4f4f4;width:60px;height:36px;border-radius:4px;text-align:center;font-size:14px;color:#999;outline:none;border:0 solid #fff}.property-layout .canvas-setting .panel-item .input-area input[data-v-16c46d15]:focus{width:58px;height:34px;border:1px solid var(--primary)}.property-layout .canvas-setting .panel-item.panel-item-bg-color .input-area input[data-v-16c46d15],.property-layout .canvas-setting .panel-item.panel-item-bg-color .input-area input[data-v-16c46d15]:focus{width:100px}.debug-layout[data-v-a4c62e20]{width:1px;background-color:#2c3e50;color:#f1f1f1;position:absolute;top:0;z-index:100;overflow:hidden;cursor:default;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;-webkit-transition:left .4s ease-in-out;transition:left .4s ease-in-out;width:420px;height:100vh;overflow:auto;left:-419px}.debug-layout pre[data-v-a4c62e20]{-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto}.debug-layout.active[data-v-a4c62e20]{left:0}.iv-modal .ivu-modal-mask{background-color:#5edfff;background-color:#dcdfe6}.vertical-center-modal{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.vertical-center-modal .ivu-modal{top:0}.code[data-v-2f10b55e],.code[data-v-3f6e72c3],.code[data-v-6fb1b24c],.code[data-v-604543c2],.code[data-v-d1df5c38],.code[data-v-d2523e40]{text-align:left;overflow:auto;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;cursor:text;max-height:500px}.top-bar-layout[data-v-287a91b7]{height:58px;background:#fff;border-bottom:0 solid #ff7671;z-index:100;position:absolute;min-width:inherit;width:100%;-webkit-box-shadow:0 1px 4px hsla(0,0%,88.2%,.5);box-shadow:0 1px 4px hsla(0,0%,88.2%,.5)}.top-bar-layout .left-bar[data-v-287a91b7]{height:58px;line-height:58px;float:left;padding-left:12px}.top-bar-layout .left-bar .main-title[data-v-287a91b7]{font-size:20px;font-weight:bolder;display:inline-block;margin-right:10px}.top-bar-layout .left-bar .btn-icon[data-v-287a91b7]{padding:8px 10px;font-size:20px;border-radius:4px;cursor:pointer;margin-right:4px}.top-bar-layout .left-bar .btn-icon[data-v-287a91b7]:hover{background-color:rgba(44,62,80,.12549019607843137)}.top-bar-layout .left-bar .unused[data-v-287a91b7]{cursor:unset}.top-bar-layout .left-bar .text-saved[data-v-287a91b7]{color:rgba(0,0,0,.4666666666666667);font-size:14px}.top-bar-layout .right-bar[data-v-287a91b7]{height:58px;line-height:58px;float:right;padding-right:30px;display:-webkit-box;display:-ms-flexbox;display:flex;width:168px;-webkit-box-pack:space-evenly;-ms-flex-pack:space-evenly;justify-content:space-evenly}.top-bar-layout .right-bar .iconfont[data-v-287a91b7]{font-size:30px;color:#2c3e50;display:block}.top-bar-layout .right-pro[data-v-287a91b7]{display:-webkit-box;display:-ms-flexbox;display:flex;float:right;line-height:58px;height:58px;width:360px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.top-bar-layout .right-xxx[data-v-287a91b7]{display:-webkit-box;display:-ms-flexbox;display:flex;width:160px;-webkit-box-pack:space-evenly;-ms-flex-pack:space-evenly;justify-content:space-evenly}.top-bar-layout .right-xxx a[data-v-287a91b7]{color:#00b07e}.feedback[data-v-287a91b7]{width:100%}.kf-qrcode[data-v-287a91b7]{width:200px;margin:60px auto;display:block}.pro-logo[data-v-287a91b7]{display:block;text-decoration:none;font-size:16px;font-weight:700}body,html{padding:0;margin:0}*{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}:root{--blue:#2c8ef8;--indigo:#727cf5;--purple:#6b5eae;--pink:#ff787b;--red:#fa5c7c;--orange:#fd7e14;--yellow:#ffbc00;--green:#0acf97;--teal:#02a8b5;--cyan:#39afd1;--white:#fff;--gray:#98a6ad;--gray-dark:#343a40;--primary:#2c8ef8;--secondary:#6c757d;--success:#0acf97;--info:#39afd1;--warning:#ffbc00;--danger:#fa5c7c;--light:#e3eaef;--dark:#313a46;--text:#333;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:"Nunito",sans-serif;--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.poster-layout{width:100%;height:100%;overflow:hidden;position:relative}.layout-wrapper{background-color:#f1f3f7;position:absolute;top:58px;left:0;width:100%;height:calc(100% - 58px);overflow:hidden}.home[data-v-21eff840]{width:100%;height:100%}.container{width:100vw;height:100vh;background-color:#f1f3f7}.container .top{height:80px;-webkit-box-shadow:0 1px 4px hsla(0,0%,88.2%,.5);box-shadow:0 1px 4px hsla(0,0%,88.2%,.5);line-height:80px;padding-left:40px}.container .nav-bar,.container .top{background-color:#2c3e50;z-index:10000;color:#fff;font-weight:700;font-size:20px}.container .nav-bar{height:calc(100vh - 80px);width:200px;position:absolute;left:0;top:80px}.container .nav-bar .nav-item{padding-left:20px;margin-top:10px}.container .nav-bar .nav-item:hover{background-color:#fff;color:#2c3e50}.container .content-container{height:calc(100% - 80px);position:relative;overflow:hidden;padding-left:200px}.container .content-container iframe{width:100%;height:100%;border:none}
--------------------------------------------------------------------------------
/server/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psoho/fast-poster/9f5fd7705c4b2107d4a9ff880d042c217c6ab757/server/static/favicon.ico
--------------------------------------------------------------------------------
/server/static/fonts/ionicons.143146fa.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psoho/fast-poster/9f5fd7705c4b2107d4a9ff880d042c217c6ab757/server/static/fonts/ionicons.143146fa.woff2
--------------------------------------------------------------------------------
/server/static/fonts/ionicons.99ac3308.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psoho/fast-poster/9f5fd7705c4b2107d4a9ff880d042c217c6ab757/server/static/fonts/ionicons.99ac3308.woff
--------------------------------------------------------------------------------
/server/static/fonts/ionicons.d535a25a.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/psoho/fast-poster/9f5fd7705c4b2107d4a9ff880d042c217c6ab757/server/static/fonts/ionicons.d535a25a.ttf
--------------------------------------------------------------------------------
/server/static/img/no-img.3679ff87.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/static/index.html:
--------------------------------------------------------------------------------
1 | fastposter海报生成器
--------------------------------------------------------------------------------
/server/static/js/about.01fcc122.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["about"],{8166:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},s=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"about"},[n("h1",[t._v("This is an about page")])])}],u=n("2877"),c={},i=Object(u["a"])(c,a,s,!1,null,null,null);e["default"]=i.exports}}]);
--------------------------------------------------------------------------------
/server/static/js/app.32a1d95f.js:
--------------------------------------------------------------------------------
1 | (function(t){function e(e){for(var r,i,s=e[0],c=e[1],u=e[2],l=0,f=[];l>>: OK")},cancel:function(){},handleSubmit:function(t){var e=this,n=this;this.$refs[t].validate((function(t){t?n.$http.post("api/login",n.formLogin,{headers:{"content-type":"application/x-www-form-urlencoded"}}).then((function(t){var r=t.data;0===r.code?(e.$Message.info(r.msg),n.login(r),console.info("关闭登录窗口"),c["a"].replace({name:"home"}),location.reload()):alert(r.msg)})):e.$Message.error("Fail!")}))}})},f=p,m=(n("f59e"),n("8f93"),n("2877")),d=Object(m["a"])(f,r,o,!1,null,null,null);e["default"]=d.exports},"25d7":function(t,e,n){"use strict";n("c927")},"261c":function(t,e,n){},"32cc":function(t,e,n){"use strict";n("8137")},"33c1":function(t,e,n){"use strict";n("ed5c")},3566:function(t,e,n){"use strict";n("b790")},3791:function(t,e,n){"use strict";n("899c")},"37a5":function(t,e,n){"use strict";n("e8b9")},"37f3":function(t,e,n){},"41cb":function(t,e,n){"use strict";n("7f7f");var r=n("2b0e"),o=n("8c4f"),a=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"home"},[n("poster-layout")],1)},i=[],s=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"poster-layout"},[n("top-bar-layout"),n("div",{staticClass:"layout-wrapper"},[n("tool-layout"),n("content-wrapper"),n("setting-layout")],1),n("debug-layout")],1)},c=[],u=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content-warpper",on:{click:t.contentWrapperClick}},[n("div",{staticClass:"canvas-wrapper",class:{"sidebar-extend":t.sidebar.showExtendBar},attrs:{tabindex:"1"},on:{keydown:function(e){return t.keyupHandler(e)},click:t.deactiveAllItem}},[n("div",{staticClass:"canvas-content-wrapper canvas-editor-wrapper",style:{width:t.w,height:t.h,"background-color":t.BGC,"background-image":t.bgi,"background-repeat":"none","background-size":t.bgs}},[n("div",{staticClass:"canvas-content",on:{contextmenu:function(e){return e.preventDefault(),t.handleContextMenu.apply(null,arguments)},click:function(e){return t.switchPanel("canvas")}}},[t._l(t.items,(function(t,e){return n("poster-item",{key:t.uuid,attrs:{item:t}})})),n("content-menu")],2)]),n("div",{staticClass:"canvas-tool-bar"},[n("div",{staticClass:"item scale-area"},[n("i",{staticClass:"icon iconfont icon-minus",attrs:{title:"缩小"},on:{click:t.shrink}}),n("p",{staticClass:"scale-num"},[t._v(t._s(t._f("formatPrcent")(t.scale)))]),n("i",{staticClass:"icon iconfont icon-plus",attrs:{title:"放大"},on:{click:t.magnify}})]),t._e()])])])},l=[],p=(n("8e6e"),n("ac6a"),n("456d"),n("6762"),n("2fdb"),n("f559"),n("bd86")),f=(n("c5f6"),function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"poster-item",attrs:{tabindex:t.item.z,id:t.idKey},on:{click:function(t){t.stopPropagation()},keydown:function(e){return t.keyupHandler(e)},contextmenu:function(e){return e.preventDefault(),t.handleContextMenu(t.item.uuid)}}},[n("vue-drag-resize",{staticClass:"poster-item-vue-drag",style:{"z-index":t.item.z},attrs:{parentLimitation:!0,parentW:t.W,parentH:t.H,w:t.w,h:t.h,x:t.x,y:t.y,minw:t.minw,minh:t.minh,isActive:t.active,aspectRatio:t.aspectRatio},on:{click:function(t){t.stopPropagation()},clicked:function(e){return t.activeItemHandler(t.item.uuid)},resizing:t.resizing,dragging:t.dragging,resizestop:t.resizestop,dragstop:t.dragstop,activated:t.activated,deactivated:t.deactivated}},["text"===t.item.t?n("p",{staticStyle:{height:"100%",width:"100%",border:"none",resize:"none",padding:"0",margin:"0",overflow:"hidden","line-height":"130%"},style:{"background-color":t.item.bgc,color:t.item.c,"font-size":t.fontSize},domProps:{textContent:t._s(t.item.v)}}):t._e(),"image"===t.item.t?n("item-image",{attrs:{item:t.item}}):t._e(),"qrcode"===t.item.t?n("item-qrcode",{attrs:{item:t.item}}):t._e(),"avatar"===t.item.t?n("item-avatar",{attrs:{item:t.item}}):t._e()],1)],1)}),m=[],d=(n("d263"),n("3b58")),A=n.n(d),h=n("2f62"),v=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("img",{staticStyle:{"border-radius":"50%"},style:{width:t.w,height:t.h,border:t.borderStyle},attrs:{src:t.imgUrl,onerror:t.defaultImg}})},b=[];function g(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function w(t){for(var e=1;e确定要切换海报[".concat(t.name," ]吗?"),onOk:function(){e.cp=t,e.changePoster(t)}})}},deletePoster:function(t){var e=this;this.$Modal.confirm({title:"温馨提示",content:"确定要删除海报[".concat(t.name," ]吗?
"),onOk:function(){e.$http.delete("api/user/posters/"+t.id,{},{headers:{"content-type":"application/x-www-form-urlencoded"}}).then((function(t){var n=t.data;e.reloadMyPoster(),e.$Message.info(n.msg)}))}})},copyPoster:function(t){var e=this;console.info("复制海报: "+t.name),this.$http.post("api/user/posters/copy/"+t.id,{},{headers:{"content-type":"application/x-www-form-urlencoded"}}).then((function(t){var n=t.data;e.reloadMyPoster(),e.$Message.info(n.msg)}))}})},vt=ht,bt=(n("1272"),Object(j["a"])(vt,ft,mt,!1,null,"577ecc48",null)),gt=bt.exports,wt=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"setting-layer",class:{active:t.item.uuid==t.ciuuid},style:{order:t.item.z},attrs:{tabindex:"1",id:t.idKey,title:"按上下方向键移动元素层次"},on:{click:t.clickHandler,keydown:t.keypressHandler}},[n("span",{staticClass:"mr8 icon iconfont",class:["icon-"+t.item.t]}),n("span",{class:{warn:!t.item.vd}},[t._v(t._s(t._f("fvd")(t.item.vd)))])])},yt=[];function Ot(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function jt(t){for(var e=1;e=2&&t<=4?"点击添加到设计器":e},isExtendItem:function(t){return 0==this.activeIndex||1==this.activeIndex},choiceItem:function(t,e){this.activeIndex=e,t.t&&this.addItem({t:t.t,name:t.name}),this.isExtendItem(e)&&(this.showExtendBarMyPosters="我的海报"===t.name&&!this.showExtendBarMyPosters,this.showExtendBarLevels="图层"===t.name&&!this.showExtendBarLevels,this.sidebar.showExtendBar=this.showExtendBarMyPosters||this.showExtendBarLevels),"我的海报"===t.name&&this.showExtendBarMyPosters&&this.reloadMyPoster(),t.name}})},kt=Et,St=(n("729a"),Object(j["a"])(kt,ut,lt,!1,null,"9d9eaeae",null)),Mt=St.exports,Qt=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"property-layout"},[n("div",{staticClass:"title"},[n("div",{staticClass:"item",class:{active:"item"==t.currentPanel},on:{click:function(e){return t.switchPanel("item")}}},[n("p",[t._v("属性设置")])]),n("div",{staticClass:"item",class:{active:"canvas"==t.currentPanel},on:{click:function(e){return t.switchPanel("canvas")}}},[n("p",[t._v("海报设置")])])]),n("div",{directives:[{name:"show",rawName:"v-show",value:"canvas"==t.currentPanel,expression:"currentPanel == 'canvas' "}],staticClass:"canvas-setting"},[n("div",{staticClass:"panel"},[n("Row",{staticClass:"mt10"},[n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("\n UUID\n ")]),n("Col",{attrs:{span:"16"}},[n("Input",{attrs:{readonly:""},model:{value:t.$store.state.p.uuid,callback:function(e){t.$set(t.$store.state.p,"uuid",e)},expression:"$store.state.p.uuid"}})],1)],1),n("Row",{staticClass:"mt10"},[n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("\n 名称\n ")]),n("Col",{attrs:{span:"16"}},[n("Input",{model:{value:t.$store.state.p.name,callback:function(e){t.$set(t.$store.state.p,"name",e)},expression:"$store.state.p.name"}})],1)],1),n("Row",[n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("\n 格式\n ")]),n("Col",{staticClass:"lh32",attrs:{span:"16"}},[n("RadioGroup",{model:{value:t.$store.state.p.type,callback:function(e){t.$set(t.$store.state.p,"type",e)},expression:"$store.state.p.type"}},[n("Radio",{attrs:{label:"png"}},[n("span",[t._v("png")])]),n("Radio",{attrs:{label:"jpeg"}},[n("span",[t._v("jpeg")])])],1)],1)],1),n("Row",{directives:[{name:"show",rawName:"v-show",value:"jpeg"===t.$store.state.p.type,expression:"$store.state.p.type === 'jpeg'"}]},[n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("\n 质量\n ")]),n("Col",{attrs:{span:"16"}},[n("Slider",{attrs:{max:100,min:30},model:{value:t.$store.state.p.quality,callback:function(e){t.$set(t.$store.state.p,"quality",e)},expression:"$store.state.p.quality"}})],1)],1),n("Divider",{attrs:{orientation:"left"}},[n("Tooltip",{attrs:{content:"修改海报尺寸"}},[t._v("\n 海报尺寸\n ")])],1),n("Row",{staticClass:"mt20"},[n("Col",{attrs:{span:"6"}},[t._v(" ")]),n("Col",{attrs:{span:"6"}},[t._v(" ")]),n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("宽度")]),n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("高度")])],1),n("Row",[n("Col",{attrs:{span:"12"}},[n("Dropdown",{on:{"on-click":t.itemChange}},[n("Button",{attrs:{type:"primary"}},[t._v("\n 常用尺寸\n "),n("Icon",{attrs:{type:"ios-arrow-down"}})],1),n("DropdownMenu",{attrs:{slot:"list"},slot:"list"},[n("DropdownItem",{attrs:{name:"640,1008"}},[t._v("640 x 1008")]),n("DropdownItem",{attrs:{name:"720,1280"}},[t._v("720 x 1280")]),n("DropdownItem",{attrs:{name:"750,1181"}},[t._v("750 x 1181")]),n("DropdownItem",{attrs:{name:"750,1333"}},[t._v("750 x 1333")]),n("DropdownItem",{attrs:{name:"800,2000"}},[t._v("800 x 2000")]),n("DropdownItem",{attrs:{name:"1242,2208"}},[t._v("1242 x 2208")])],1)],1)],1),n("Col",{attrs:{span:"6"}},[n("InputNumber",{staticClass:"input ",attrs:{max:1e3,min:50},model:{value:t.$store.state.p.w,callback:function(e){t.$set(t.$store.state.p,"w",e)},expression:"$store.state.p.w"}})],1),n("Col",{attrs:{span:"6"}},[n("InputNumber",{staticClass:"input ",attrs:{max:2e3,min:50},model:{value:t.$store.state.p.h,callback:function(e){t.$set(t.$store.state.p,"h",e)},expression:"$store.state.p.h"}})],1)],1),n("Divider",{attrs:{orientation:"left"}},[n("Tooltip",{attrs:{content:"修改背景颜色和图片"}},[t._v("\n 背景\n ")])],1),n("Row",{staticClass:"mt10"},[n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("\n 背景色\n ")]),n("Col",{attrs:{span:"6"}},[n("ColorPicker",{attrs:{recommend:""},model:{value:t.$store.state.p.bgc,callback:function(e){t.$set(t.$store.state.p,"bgc",e)},expression:"$store.state.p.bgc"}})],1)],1),n("Row",{staticClass:"mt10"},[n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("\n 背景图\n ")]),n("Col",{attrs:{span:"8"}},[n("Upload",{attrs:{action:t.UPLOAD_URL,"on-success":t.successHandler,headers:{token:t.token},format:["jpg","jpeg","png"],"show-upload-list":!1}},[n("Button",{attrs:{type:"primary",icon:"ios-cloud-upload-outline"}},[t._v("上传")])],1)],1),n("Col",{attrs:{span:"4"}},[n("Button",{attrs:{type:"primary"},on:{click:t.deleteBgImage}},[t._v("删除背景图")])],1)],1),n("Row",{directives:[{name:"show",rawName:"v-show",value:t.$store.state.p.bgUrl,expression:"$store.state.p.bgUrl"}]},[n("Col",{attrs:{span:"6"}},[t._v("\n 背景图地址\n ")]),n("Col",{attrs:{span:"17"}},[n("Input",{attrs:{type:"textarea",rows:4},model:{value:t.$store.state.p.bgUrl,callback:function(e){t.$set(t.$store.state.p,"bgUrl",e)},expression:"$store.state.p.bgUrl"}})],1)],1)],1)]),n("setting-item-layout",{directives:[{name:"show",rawName:"v-show",value:"item"==t.currentPanel,expression:"currentPanel == 'item' "}],staticClass:"item-setting"})],1)},Rt=[],Yt=(n("28a5"),function(){var t=this,e=t.$createElement,n=t._self._c||e;return null!=t.currentItem?n("div",{staticClass:"item-setting-panel"},[n("setting-item-base",{attrs:{item:t.currentItem}})],1):t._e()}),Ht=[],Wt=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"setting-item-base"},[n("Divider",{attrs:{orientation:"left"}},[n("Tooltip",{attrs:{"max-width":"200",content:"此处可进行位置和大小的调整"}},[t._v("\n 位置大小\n ")])],1),n("Row",[n("Col",{attrs:{span:"6"}},[t._v("x")]),n("Col",{attrs:{span:"6"}},[t._v("y")]),n("Col",{attrs:{span:"6"}},[t._v("宽度")]),n("Col",{attrs:{span:"6"}},[t._v("高度")])],1),n("Row",[n("Col",{attrs:{span:"6"}},[n("InputNumber",{staticClass:"input ",attrs:{max:t.w-t.item.w,min:0},model:{value:t.item.x,callback:function(e){t.$set(t.item,"x",e)},expression:"item.x"}})],1),n("Col",{attrs:{span:"6"}},[n("InputNumber",{staticClass:"input ",attrs:{max:t.h-t.item.h,min:0},model:{value:t.item.y,callback:function(e){t.$set(t.item,"y",e)},expression:"item.y"}})],1),n("Col",{attrs:{span:"6"}},[n("InputNumber",{staticClass:"input ",attrs:{max:t.w,min:0},model:{value:t.item.w,callback:function(e){t.$set(t.item,"w",e)},expression:"item.w"}})],1),n("Col",{attrs:{span:"6"}},[n("InputNumber",{staticClass:"input ",attrs:{max:t.h,min:0},model:{value:t.item.h,callback:function(e){t.$set(t.item,"h",e)},expression:"item.h"}})],1)],1),n("Divider",{attrs:{orientation:"left"}},[n("Tooltip",{attrs:{"max-width":"200",content:"点击修改颜色"}},[t._v("颜色")])],1),n("Row",[n("Col",{staticClass:"lh32",attrs:{span:"6"}},[t._v("\n 颜色\n ")]),n("Col",{attrs:{span:"6"}},[n("color-picker",{attrs:{recommend:""},on:{"on-change":t.changeColor},model:{value:t.item.c,callback:function(e){t.$set(t.item,"c",e)},expression:"item.c"}})],1)],1),n("div",{directives:[{name:"show",rawName:"v-show",value:"image"===t.item.t||"avatar"===t.item.t,expression:"item.t === 'image' || item.t === 'avatar' "}]},[n("Divider",{attrs:{orientation:"left"}},[n("Tooltip",{attrs:{"max-width":"200",content:"图片"}},[t._v("图片")])],1),n("Row",{staticClass:"mt10"},[n("Col",{staticClass:"lh32",attrs:{span:"4"}},[t._v("\n 图片\n ")]),n("Col",{attrs:{span:"6"}},[n("Upload",{attrs:{action:t.UPLOAD_URL,"on-success":t.successHandler,headers:{token:t.token},format:["jpg","jpeg","png"],"show-upload-list":!1}},[n("Button",{attrs:{type:"primary"}},[t._v("上传")])],1)],1),n("Col",{attrs:{span:"8"}},[n("Button",{on:{click:t.changeImgSize}},[t._v("原始尺寸")])],1),n("Col",{attrs:{span:"4"}},[n("Button",{on:{click:t.deleteImg}},[t._v("删除")])],1)],1)],1),n("div",{directives:[{name:"show",rawName:"v-show",value:"text"===t.item.t,expression:"item.t === 'text' "}]},[n("Divider",{attrs:{orientation:"left"}},[n("Tooltip",{attrs:{"max-width":"200",content:"拖动修改字体大小"}},[t._v("字体")])],1),n("Row",[n("Col",{staticClass:"lh36",attrs:{span:"6"}},[t._v("\n 字体大小\n ")]),n("Col",{attrs:{span:"10"}},[n("Slider",{attrs:{min:10},model:{value:t.item.s,callback:function(e){t.$set(t.item,"s",e)},expression:"item.s"}})],1),n("Col",{attrs:{span:"1"}},[t._v("\n \n ")]),n("Col",{attrs:{span:"6"}},[n("InputNumber",{staticClass:"input ",attrs:{max:100,min:0},model:{value:t.item.s,callback:function(e){t.$set(t.item,"s",e)},expression:"item.s"}})],1)],1)],1),n("div",[n("Divider",{attrs:{orientation:"left"}},[n("Tooltip",{attrs:{"max-width":"200",content:"静态参数: 效果预览使用,不可变 动态参数: 调用接口使用,可变"}},[t._v("\n 参数\n ")])],1),n("Row",[n("Col",{attrs:{span:"6"}},[t._v("\n 名称\n ")]),n("Col",{attrs:{span:"17"}},[n("Input",{model:{value:t.item.vd,callback:function(e){t.$set(t.item,"vd",e)},expression:"item.vd"}})],1)],1),n("Row",[n("Col",{attrs:{span:"6"}},[t._v("\n 备注\n ")]),n("Col",{attrs:{span:"17"}},[n("Input",{model:{value:t.item.name,callback:function(e){t.$set(t.item,"name",e)},expression:"item.name"}})],1)],1),n("Row",[n("Col",{attrs:{span:"6"}},[t._v("\n 默认\n ")]),n("Col",{attrs:{span:"17"}},[n("Input",{attrs:{type:"textarea",rows:4},on:{"on-change":t.changeV},model:{value:t.item.v,callback:function(e){t.$set(t.item,"v",e)},expression:"item.v"}})],1)],1)],1)],1)},Kt=[];function Gt(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function Jt(t){for(var e=1;e=1},set:function(t){this.item.st=t?Number((this.item.s/10).toFixed(0)):0}}}),methods:{isQrcode:function(){return"qrcode"===this.item.t},changeColor:function(t){this.isQrcode()&&S(this.item.uuid,"buildQrcode")},changeBgColor:function(t){this.isQrcode()&&S(this.item.uuid,"buildQrcode")},deleteImg:function(t){this.item.v=""},changeV:function(){this.isQrcode()&&S(this.item.uuid,"buildQrcode")},changeImgSize:function(){var t=this.item,e=new Image;e.src=t.v.startsWith("http")?t.v:API_URL+t.v,e.onload=function(){t.w=e.width,setTimeout((function(){t.h=e.height}),10)}},successHandler:function(t,e){var n=this;if(0!==t.code);else{var r=t.data.url,o=new Image;o.src=r.startsWith("http")?r:API_URL+r;var a=400,i=setInterval((function(){a--<=0&&window.clearInterval(i),o.width>0&&(n.item.v=r,window.clearInterval(i))}),50)}}}},Lt=Ut,Ft=(n("db58"),Object(j["a"])(Lt,Wt,Kt,!1,null,null,null)),Nt=Ft.exports;function Vt(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function Tt(t){for(var e=1;e0&&(n.changeBgImage(r),n.changeWH({w:o.width,h:o.height}),window.clearInterval(i))}),50)}}})},ee=te,ne=(n("ffea"),n("a8c3"),Object(j["a"])(ee,Qt,Rt,!1,null,"16c46d15",null)),re=ne.exports,oe=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"debug-layout",class:{active:t.show},on:{dblclick:function(e){t.show=!t.show}}},[n("pre",[t._v(t._s(t.json))])])},ae=[];function ie(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function se(t){for(var e=1;e'),t="["+t.substring(1,t.length-1)+"]";var e='buildPoster("').concat(this.posterUuid,'", $params)->save("demo.png");');return e}}},Se=ke,Me=(n("3791"),Object(j["a"])(Se,xe,Be,!1,null,"6fb1b24c",null)),Qe=Me.exports,Re=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticStyle:{"text-align":"left"}},[n("pre",{staticClass:"code language-shell line-numbers",domProps:{innerHTML:t._s(t.html)}},[t._v(" "),n("span"),t._v("\n ")]),n("Button",{attrs:{id:"btnCopyCurl",type:"primary"}},[t._v("复制")])],1)},Ye=[];n("8009");function He(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function We(t){for(var e=1;e{\n // 将res 信息直接复制到 img 标签的 src 属性上即可\n // document.getElementById('myImg').src = res\n})\n");return e}}},nn=en,rn=(n("25d7"),Object(j["a"])(nn,Ze,_e,!1,null,"d2523e40",null)),on=rn.exports,an=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticStyle:{"text-align":"left"}},[n("pre",{staticClass:"code language-shell line-numbers",domProps:{innerHTML:t._s(t.html)}},[t._v(" "),n("span"),t._v("\n ")]),n("Button",{attrs:{id:"btnCopyGo",type:"primary"}},[t._v("复制")]),n("a",{attrs:{href:t.link}},[t._v(t._s(t.link))])],1)},sn=[];n("1989");function cn(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function un(t){for(var e=1;e请在新建海报前,保存当前修改。",onOk:function(){t.newPoster()}})},feedback:function(){this.m.showFeedbackModal=!0},preview:function(){var t=this.$refs.showImg,e=this,n=this.json,r=new XMLHttpRequest;r.withCredentials=!0,r.open("POST",window.PREVIEW_URL,!0),r.setRequestHeader("Content-Type","application/json"),r.setRequestHeader("token",localStorage.getItem("fptoken")),r.responseType="blob",r.onload=function(){if(200===this.status){var n=this.response;t.onload=function(t){var n=e.$el.querySelector(".images").$viewer;n.show()},t.src=window.URL.createObjectURL(n)}401===this.status&&alert("请登录后再操作")},r.send(n)}})},vn=hn,bn=(n("5222"),Object(j["a"])(vn,fe,me,!1,null,"287a91b7",null)),gn=bn.exports,wn={name:"PosterLayout",components:{TopBarLayout:gn,ContentWrapper:ct,ToolLayout:Mt,SettingLayout:re,DebugLayout:pe},mounted:function(){window.app=this},methods:{}},yn=wn,On=(n("607f"),Object(j["a"])(yn,s,c,!1,null,null,null)),jn=On.exports,Cn={name:"home",components:{PosterLayout:jn},mounted:function(){},computed:{},data:function(){return{}}},Pn=Cn,In=(n("37a5"),Object(j["a"])(Pn,a,i,!1,null,"21eff840",null)),xn=In.exports,Bn=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"container"},[n("div",{staticClass:"top"},[t._v("\n 演示以Vue组件方式嵌入系统中\n ")]),n("div",{staticClass:"nav-bar"},t._l(10,(function(e){return n("div",{staticClass:"nav-item"},[t._v("\n 菜单: "+t._s(e)+"\n ")])})),0),n("div",{staticClass:"content-container"},[n("poster-layout")],1)])},Dn=[],En={name:"B",components:{PosterLayout:jn},mounted:function(){},methods:{}},kn=En,Sn=(n("6a03"),Object(j["a"])(kn,Bn,Dn,!1,null,null,null)),Mn=Sn.exports,Qn=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"container"},[n("div",{staticClass:"top"},[t._v("\n 演示以Iframe方式嵌入系统中(推荐使用)\n ")]),n("div",{staticClass:"nav-bar"},t._l(10,(function(e){return n("div",{staticClass:"nav-item"},[t._v("\n 菜单: "+t._s(e)+"\n ")])})),0),t._m(0)])},Rn=[function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content-container"},[n("iframe",{attrs:{src:"/"}})])}],Yn={name:"B2",components:{},mounted:function(){},methods:{}},Hn=Yn,Wn=(n("f2ee"),Object(j["a"])(Hn,Qn,Rn,!1,null,null,null)),Kn=Wn.exports;r["default"].use(o["a"]);var Gn=new o["a"]({routes:[{path:"/",name:"home",component:xn},{path:"/login",name:"login",component:function(){return n.e("about").then(n.bind(null,"1345"))}},{path:"/b",name:"b",component:Mn},{path:"/b2",name:"b2",component:Kn},{path:"/about",name:"about",component:function(){return n.e("about").then(n.bind(null,"8166"))}}]});Gn.beforeEach((function(t,e,n){0===t.matched.length?e.name?n({name:e.name}):n("/"):n()}));e["a"]=Gn},"46de":function(t,e,n){"use strict";n("e15c")},"49ac":function(t,e,n){"use strict";n("37f3")},5222:function(t,e,n){"use strict";n("cdcd")},5405:function(t,e,n){},"55c4":function(t,e,n){"use strict";n("d54c")},"56d7":function(t,e,n){"use strict";n.r(e);n("cadf"),n("551c"),n("f751"),n("097d");var r=n("2b0e"),o=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{attrs:{id:"app"}},[n("router-view")],1)},a=[],i=(n("7c55"),n("2877")),s={},c=Object(i["a"])(s,o,a,!1,null,null,null),u=c.exports,l=n("41cb"),p=(n("8e6e"),n("ac6a"),n("456d"),n("bd86")),f=n("2f62");n("f559"),n("7f7f"),n("c5f6"),n("55dd"),n("3b2b"),n("28a5"),n("6b54"),n("a481");function m(t,e){var n,r,o="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),a=[];if(e=e||o.length,t)for(n=0;n=10)console.error("对不起, 暂时只支持添加10个元素。请联系客服开通权限。");else{var n={uuid:d(),x:400,y:200,w:150,h:150,z:1,s:15,c:"#000000",bgc:"",v:"https://poster.prodapi.cn/static/images/xiaoniu.png",vd:"",fn:"",st:0,active:!1};n=B(B({},e),n),n.x=~~(t.p.w/2-n.w/2),n.y=~~(t.p.h/2-n.h/2),"text"===n.t&&(n.w=324,n.h=58,n.s=24,n.x=120,n.y=200,n.v="多行文本测试1多行文本测试2多行文本测试3多行文本测试4"),"image"===n.t&&(n.w=200,n.h=200),"qrcode"===n.t&&(n.p=0,n.c="#000000",n.bgc="#ffffff",n.v="https://fastposter.net/#from=qrcode"),"avatar"===n.t&&(n.w=80,n.h=80,n.c="#888888");var r=Math.max.apply(Math,t.p.items.map((function(t){return t.z})));r>=1&&(n.z=r+1),t.p.items.push(n),G.commit("activeItemAndShowProperty",n.uuid)}},copyItem:function(t,e){var n=t.p.items.filter((function(t){return t.uuid===e}))[0];t.copyItem=B({},n)},pasteItem:function(t,e){if(t.copyItem){t.copyItem.uuid=d();var n=t.copyItem;n.x=n.x+24,n.y=n.y+24;var r=Math.max.apply(Math,t.p.items.map((function(t){return t.z})));r>=1&&(n.z=r+1),t.p.items.push(n),G.commit("activeItemAndShowProperty",n.uuid)}},deactiveAllItem:function(t,e){t.p.items.forEach((function(t){return t.active=!1}))},activeItemDontShow:function(t,e){G.commit("activeItem",e),t.p.editor.cp="item"},activeItemAndShowProperty:function(t,e){G.commit("activeItem",e),t.p.editor.cp="item"},activeItem:function(t,e){t.p.editor.ciuuid!==e&&(t.p.items.forEach((function(t){t.active=t.uuid===e})),t.p.editor.ciuuid=e)},removeItem:function(t,e){var n=[];t.p.items.forEach((function(t){t.uuid!==e&&n.push(t)})),n.length>=1&&(n[0].active=!0,t.p.editor.ciuuid=n[0].uuid),t.p.items=n},switchPanel:function(t,e){t.p.editor.cp=e},adjustScale:function(t,e){var n=t.p.editor.scale;n+=e,n=Number(n.toFixed(2)),nD?console.warn("缩放比达到极限"):(t.p.editor.scale=n,console.info("调整缩放比例: scale="+n))},autoScale:function(t,e){t.p.scale=.6},itemUp:function(t,e){var n=t.p.items.filter((function(t){return t.uuid===e}))[0],r=n.z,o=Math.max.apply(Math,t.p.items.map((function(t){return t.z})));if(o!==r){var a=r+1,i=t.p.items.filter((function(t){return t.z===a&&t.uuid!==e}));if(i&&0!==i.length){var s=i[0];n.z=a,s.z=r}else console.warn("元素上移(没有匹配的元素): z="+r+", uuid="+e)}else console.warn("元素上移(元素已经到了最上面): z="+r+", uuid="+e)},itemDown:function(t,e){var n=t.p.items.filter((function(t){return t.uuid===e}))[0],r=n.z;if(!(r<=1)){var o=r-1,a=t.p.items.filter((function(t){return t.z===o&&t.uuid!==e}));if(a&&0!==a.length){var i=a[0];n.z=o,i.z=r}}},changeWH:function(t,e){t.p.w=e.w,t.p.h=e.h},changeBgImage:function(t,e){t.p.bgUrl=e},login:function(t,e){localStorage.setItem("fptoken",e.token)},logout:function(t,e){t.user=null,localStorage.removeItem("fptoken")},changePoster:function(t,e){var n=e.id,r=e.name,o=JSON.parse(e.json);o.items.forEach((function(t){t.uuid=t.uuid?t.uuid:d()})),o.editor=o.editor?o.editor:t.p.editor,o.editor.scaleNew=k,o.editor.sidebar=o.editor.sidebar?o.editor.sidebar:t.p.editor.sidebar,o.editor.saveStatus=o.editor.saveStatus?o.editor.saveStatus:"draft",o.id=n,o.name=r,o.uuid=e.code,t.p=o,window.setTimeout((function(){G.commit("adjustScale",.01)}),100),window.setTimeout((function(){G.commit("adjustScale",-.01)}),200)},newPoster:function(t,e){var n={name:"未命名",id:0,w:720,h:1280,bgc:"#ffffff",type:"jpeg",quality:M,bgUrl:"",items:[],editor:{scaleNew:k,scale:k,cp:"canvas",ciuuid:"",debug:S,saveStatus:"draft",sidebar:{showExtendBar:t.p.editor.sidebar.showExtendBar}}};t.p=n},reloadMyPoster:function(t,e){I.get("api/user/posters",{},{headers:{"content-type":"application/x-www-form-urlencoded"}}).then((function(e){var n=e.data;n.posters.forEach((function(t){return t.preview=(t.preview.startsWith("http")?t.preview:window.API_URL+t.preview)+"?t="+(new Date).getTime()})),t.posters=n.posters}))},savePoster:function(t,e){var n={};n.id=t.p.id,n.json=G.getters.json,n.name=t.p.name,n.quality=t.p.quality,t.p.editor.saveStatus="saving",I.post("api/user/posters",n).then((function(n){var r=n.data;0!==r.code&&alert(r.msg),r.id&&(t.p.id=r.id),t.p.editor.saveStatus="saved",G.commit("reloadMyPoster",t,e)}))}},actions:{}},R=Q,Y={state:{menuTop:0,menuLeft:0,menuShow:!1},mutations:{showContextMenu:function(t,e){var n=e.top,r=e.left;t.menuShow=!0,t.menuTop=n,t.menuLeft=r},hideContextMenu:function(t){t.menuShow=!1}}};function H(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function W(t){for(var e=1;e