admin_template.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import json
  2. from fastapi import APIRouter, Depends, Request, Form
  3. from fastapi.responses import HTMLResponse, RedirectResponse
  4. from sqlalchemy import text # 新增这一行
  5. from sqlalchemy.orm import Session
  6. from database import SessionLocal
  7. from services.template_builder import build_estimate_packet
  8. router = APIRouter(prefix="/admin/template", tags=["template"])
  9. def get_db():
  10. db = SessionLocal()
  11. try:
  12. yield db
  13. finally:
  14. db.close()
  15. @router.get("/preview", response_class=HTMLResponse)
  16. def preview_template(
  17. request: Request,
  18. base_template_id: int,
  19. db: Session = Depends(get_db)
  20. ):
  21. packet = build_estimate_packet(db, base_template_id)
  22. json_data = {
  23. "templateId": base_template_id,
  24. "template": packet
  25. }
  26. # ------- HTML 渲染 -------
  27. html = f"""
  28. <h2>模板预览(Template ID:{base_template_id})</h2>
  29. <div style="display:flex;gap:30px">
  30. <div style="width:55%">
  31. """
  32. for step in packet:
  33. html += f"""
  34. <div style="border:1px solid #ccc;padding:10px;margin-bottom:10px">
  35. <h3>Step {step['step']} - {step['stepName']}</h3>
  36. """
  37. for prop in step["properties"]:
  38. html += f"""
  39. <div style="margin-left:20px">
  40. <b>{prop['name']}</b>
  41. {"(多选)" if prop.get("isMulti") else ""}
  42. {"(必填)" if prop.get("required") else ""}
  43. <ul>
  44. """
  45. for v in prop["values"]:
  46. html += f"""
  47. <li>
  48. {v['valueText']}
  49. {"✅" if v.get("isNormal") else "❌"}
  50. </li>
  51. """
  52. html += "</ul></div>"
  53. html += "</div>"
  54. html += f"""
  55. </div>
  56. <div style="width:45%">
  57. <h3>JSON 输出</h3>
  58. <pre style="background:#f6f6f6;padding:10px;max-height:800px;overflow:auto">
  59. {json.dumps(json_data, ensure_ascii=False, indent=2)}
  60. </pre>
  61. </div>
  62. </div>
  63. """
  64. return html
  65. # ---------------- 页面 ----------------
  66. @router.get("/generate", response_class=HTMLResponse)
  67. def generate_page(request: Request, db: Session = Depends(get_db)):
  68. machines = db.execute(text("""
  69. SELECT machine_id, brand_name, name
  70. FROM t_machine
  71. ORDER BY brand_name, name
  72. LIMIT 200
  73. """)).fetchall()
  74. templates = [
  75. (99181, "iPhone 国行基础模板"),
  76. (99198, "安卓通用模板"),
  77. (99197, "折叠屏模板"),
  78. ]
  79. # ✅ 从 step1_attr 读取所有可补充的属性
  80. step1_attrs = db.execute(text("""
  81. SELECT
  82. attr_key,
  83. attr_name,
  84. MIN(sort_order) AS sort_order
  85. FROM step1_attr
  86. GROUP BY attr_key, attr_name
  87. ORDER BY sort_order
  88. """)).fetchall()
  89. html = """
  90. <h2>生成机型模板</h2>
  91. <form method="post">
  92. <h3>① 选择机型</h3>
  93. <select name="machine_id">
  94. """
  95. for m in machines:
  96. html += f'<option value="{m.machine_id}">{m.brand_name} - {m.name}</option>'
  97. html += """
  98. </select>
  99. <h3>② 选择基础模板</h3>
  100. <select name="base_template_id">
  101. """
  102. for tid, tname in templates:
  103. html += f'<option value="{tid}">{tid} - {tname}</option>'
  104. html += """
  105. </select>
  106. <h3>③ Step1 补充配置(从 step1_attr 读取,逗号分隔)</h3>
  107. """
  108. # ✅ 动态输出所有属性输入框
  109. for a in step1_attrs:
  110. html += f"""
  111. <div style="margin-bottom:10px;">
  112. {a.attr_name}({a.attr_key}):<br>
  113. <input
  114. name="manual_{a.attr_key}"
  115. placeholder="例如:A,B,C"
  116. style="width:300px;"
  117. >
  118. </div>
  119. """
  120. html += """
  121. <button type="submit">④ 生成模板</button>
  122. </form>
  123. """
  124. return html
  125. @router.get("/generate1", response_class=HTMLResponse)
  126. def generate_page(request: Request, db: Session = Depends(get_db)):
  127. machines = db.execute(text("""
  128. SELECT machine_id, brand_name, name
  129. FROM t_machine
  130. ORDER BY brand_name, name
  131. LIMIT 200
  132. """)).fetchall()
  133. templates = [
  134. (99181, "iPhone 国行基础模板"),
  135. (99198, "安卓通用模板"),
  136. (99197, "折叠屏模板"),
  137. ]
  138. html = """
  139. <h2>生成机型模板</h2>
  140. <form method="post">
  141. <h3>① 选择机型</h3>
  142. <select name="machine_id">
  143. """
  144. for m in machines:
  145. html += f'<option value="{m.machine_id}">{m.brand_name} - {m.name}</option>'
  146. html += """
  147. </select>
  148. <h3>② 选择基础模板</h3>
  149. <select name="base_template_id">
  150. """
  151. for tid, tname in templates:
  152. html += f'<option value="{tid}">{tid} - {tname}</option>'
  153. html += """
  154. </select>
  155. <h3>③ Step1 补充配置(手动输入,逗号分隔)</h3>
  156. 容量(capacity):<br>
  157. <input name="capacity" placeholder="128G,256G,512G"><br><br>
  158. 颜色(color):<br>
  159. <input name="color" placeholder="黑色,白色,蓝色"><br><br>
  160. 保修(warranty):<br>
  161. <input name="warranty" placeholder="无保修,官方保修"><br><br>
  162. <button type="submit">④ 生成模板</button>
  163. </form>
  164. """
  165. return html
  166. @router.post("/generate")
  167. def generate_submit(
  168. request: Request,
  169. machine_id: int = Form(...),
  170. base_template_id: int = Form(...),
  171. db: Session = Depends(get_db)
  172. ):
  173. form = request._form # FastAPI 已解析
  174. # 1️⃣ 基础模板(step1_attr + release_option)
  175. packet = build_estimate_packet(db, base_template_id)
  176. # 2️⃣ 补充 step1(人工输入)
  177. step1 = packet[0]["properties"]
  178. # 所有 manual_xxx
  179. manual_inputs = {}
  180. for k, v in form.items():
  181. if k.startswith("manual_"):
  182. manual_inputs[k.replace("manual_", "")] = v
  183. # 读取 step1_attr 的定义(用于取 attr_name)
  184. attr_map = db.execute(text("""
  185. SELECT attr_key, attr_name
  186. FROM step1_attr
  187. GROUP BY attr_key, attr_name
  188. """)).fetchall()
  189. attr_name_map = {a.attr_key: a.attr_name for a in attr_map}
  190. for attr_key, value_str in manual_inputs.items():
  191. if not value_str:
  192. continue
  193. values = [x.strip() for x in value_str.split(",") if x.strip()]
  194. if not values:
  195. continue
  196. step1.append({
  197. "id": attr_key,
  198. "name": attr_name_map.get(attr_key, attr_key),
  199. "required": False,
  200. "isMulti": False,
  201. "values": [
  202. {
  203. "valueId": f"manual_{attr_key}_{v}",
  204. "valueText": v,
  205. "isNormal": True
  206. }
  207. for v in values
  208. ]
  209. })
  210. # 3️⃣ 保存模板
  211. db.execute(text("""
  212. INSERT INTO machine_temp(machine_id, temp_type, estimate_packet)
  213. VALUES (:mid, '00', :json)
  214. ON DUPLICATE KEY UPDATE estimate_packet=:json
  215. """), {
  216. "mid": machine_id,
  217. "json": json.dumps({
  218. "templateId": base_template_id,
  219. "template": packet
  220. }, ensure_ascii=False)
  221. })
  222. db.commit()
  223. return RedirectResponse("/admin/template/generate", status_code=302)