system_config.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. {% extends "base.html" %}
  2. {% block title %}逐鹿人才-证件合同管理系统-系统配置{% endblock %}
  3. {% block content %}
  4. <div class="container mt-4">
  5. <!-- 系统配置表单 -->
  6. <div class="card shadow-sm mb-4">
  7. <div class="card-body">
  8. <h3 class="mb-4 fw-bold">系统配置</h3>
  9. <form method="POST">
  10. <!-- 网站信息 -->
  11. <h5 class="mb-3">网站信息</h5>
  12. <div class="mb-3">
  13. <label for="site_name" class="form-label">网站名称</label>
  14. <input type="text" class="form-control" id="site_name" name="site_name"
  15. value="{{ config.site_name }}" required>
  16. </div>
  17. <!-- 邮件配置 -->
  18. <h5 class="mb-3 mt-4">邮件服务器配置</h5>
  19. <div class="row g-3">
  20. <div class="col-md-6">
  21. <label for="mail_server" class="form-label">SMTP服务器</label>
  22. <input type="text" class="form-control" id="mail_server" name="mail_server"
  23. value="{{ config.mail_server }}" required>
  24. </div>
  25. <div class="col-md-6">
  26. <label for="mail_port" class="form-label">端口</label>
  27. <input type="number" class="form-control" id="mail_port" name="mail_port"
  28. value="{{ config.mail_port }}" required>
  29. </div>
  30. <div class="col-md-6">
  31. <label for="mail_username" class="form-label">用户名</label>
  32. <input type="text" class="form-control" id="mail_username" name="mail_username"
  33. value="{{ config.mail_username }}" required>
  34. </div>
  35. <div class="col-md-6">
  36. <label for="mail_password" class="form-label">密码</label>
  37. <input type="password" class="form-control" id="mail_password" name="mail_password"
  38. value="{{ config.mail_password }}" required>
  39. </div>
  40. </div>
  41. <div class="form-check mt-3">
  42. <input type="checkbox" class="form-check-input" id="mail_use_tls" name="mail_use_tls"
  43. {% if config.mail_use_tls %}checked{% endif %}>
  44. <label class="form-check-label" for="mail_use_tls">使用TLS加密</label>
  45. </div>
  46. <div class="d-grid gap-2 mt-4">
  47. <button type="submit" class="btn btn-primary btn-lg">保存配置</button>
  48. </div>
  49. </form>
  50. </div>
  51. </div>
  52. <!-- 定时任务配置 -->
  53. <div class="card shadow-sm mb-4">
  54. <div class="card-body">
  55. <h5 class="mb-3">合同到期提醒设置</h5>
  56. <form id="scheduler-form" method="POST" action="/system/config/scheduler">
  57. <div class="mb-3">
  58. <label class="form-label">发送频率</label>
  59. <div class="form-check">
  60. <input class="form-check-input" type="radio" name="frequency" id="frequency-daily" value="daily"
  61. {% if config.scheduler_frequency == 'daily' %}checked{% endif %}>
  62. <label class="form-check-label" for="frequency-daily">
  63. 每天发送
  64. </label>
  65. </div>
  66. <div class="form-check">
  67. <input class="form-check-input" type="radio" name="frequency" id="frequency-weekly" value="weekly"
  68. {% if config.scheduler_frequency == 'weekly' %}checked{% endif %}>
  69. <label class="form-check-label" for="frequency-weekly">
  70. 每周发送
  71. </label>
  72. </div>
  73. <div class="form-check">
  74. <input class="form-check-input" type="radio" name="frequency" id="frequency-monthly" value="monthly"
  75. {% if config.scheduler_frequency == 'monthly' %}checked{% endif %}>
  76. <label class="form-check-label" for="frequency-monthly">
  77. 每月发送
  78. </label>
  79. </div>
  80. </div>
  81. <div id="weekly-options" class="mb-3" style="display: {% if config.scheduler_frequency == 'weekly' %}block{% else %}none{% endif %};">
  82. <label class="form-label">选择星期几</label>
  83. <div class="row">
  84. {% for day in ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] %}
  85. <div class="col-4 col-md-3">
  86. <div class="form-check">
  87. <input class="form-check-input" type="checkbox" name="weekdays" id="weekday-{{ loop.index }}"
  88. value="{{ loop.index0 }}" {% if loop.index0 in config.scheduler_weekdays %}checked{% endif %}>
  89. <label class="form-check-label" for="weekday-{{ loop.index }}">{{ day }}</label>
  90. </div>
  91. </div>
  92. {% endfor %}
  93. </div>
  94. </div>
  95. <div id="monthly-options" class="mb-3" style="display: {% if config.scheduler_frequency == 'monthly' %}block{% else %}none{% endif %};">
  96. <label class="form-label">选择日期 (1-31)</label>
  97. <input type="number" class="form-control" name="month_day" min="1" max="31"
  98. value="{{ config.scheduler_month_day or 1 }}">
  99. </div>
  100. <div class="mb-3">
  101. <label class="form-label">发送时间</label>
  102. <div class="row g-2">
  103. <div class="col-4">
  104. <label class="form-label small text-muted">小时</label>
  105. <select class="form-select" name="hour">
  106. {% for h in range(0, 24) %}
  107. <option value="{{ h }}" {% if h == config.scheduler_hour %}selected{% endif %}>
  108. {{ h }}:00
  109. </option>
  110. {% endfor %}
  111. </select>
  112. </div>
  113. <div class="col-4">
  114. <label class="form-label small text-muted">分钟</label>
  115. <select class="form-select" name="minute">
  116. {% for m in range(0, 60) %}
  117. <option value="{{ m }}" {% if m == config.scheduler_minute %}selected{% endif %}>
  118. {{ m }}
  119. </option>
  120. {% endfor %}
  121. </select>
  122. </div>
  123. <div class="col-4 d-flex align-items-end">
  124. <div class="form-text">(0-59)</div>
  125. </div>
  126. </div>
  127. </div>
  128. <div class="d-grid gap-2 mt-4">
  129. <button type="submit" class="btn btn-primary">保存定时设置</button>
  130. </div>
  131. </form>
  132. </div>
  133. </div>
  134. <!-- 部门/我方主体/合同类型管理 -->
  135. <div class="row g-4">
  136. <!-- 部门管理 -->
  137. <div class="col-md-4">
  138. <div class="card shadow-sm h-100">
  139. <div class="card-header fw-bold">部门管理</div>
  140. <div class="card-body d-flex flex-column">
  141. <ul class="list-group mb-3 flex-grow-1 overflow-auto" id="department-list" style="max-height:300px;">
  142. {% for dept in departments %}
  143. <li class="list-group-item d-flex justify-content-between align-items-center">
  144. {{ dept.name }}
  145. <button class="btn btn-sm btn-danger delete-btn" data-id="{{ dept.id }}">删除</button>
  146. </li>
  147. {% endfor %}
  148. </ul>
  149. <div class="input-group mt-auto">
  150. <input type="text" id="new-department" class="form-control" placeholder="新增部门">
  151. <button class="btn btn-primary" id="add-department">添加</button>
  152. </div>
  153. </div>
  154. </div>
  155. </div>
  156. <!-- 我方主体管理 -->
  157. <div class="col-md-4">
  158. <div class="card shadow-sm h-100">
  159. <div class="card-header fw-bold">我方主体管理</div>
  160. <div class="card-body d-flex flex-column">
  161. <ul class="list-group mb-3 flex-grow-1 overflow-auto" id="entity-list" style="max-height:300px;">
  162. {% for entity in entities %}
  163. <li class="list-group-item d-flex justify-content-between align-items-center">
  164. {{ entity.name }}
  165. <button class="btn btn-sm btn-danger delete-btn" data-id="{{ entity.id }}">删除</button>
  166. </li>
  167. {% endfor %}
  168. </ul>
  169. <div class="input-group mt-auto">
  170. <input type="text" id="new-entity" class="form-control" placeholder="新增公司主体">
  171. <button class="btn btn-primary" id="add-entity">添加</button>
  172. </div>
  173. </div>
  174. </div>
  175. </div>
  176. <!-- 合同类型管理 -->
  177. <div class="col-md-4">
  178. <div class="card shadow-sm h-100">
  179. <div class="card-header fw-bold">合同类型管理</div>
  180. <div class="card-body d-flex flex-column">
  181. <ul class="list-group mb-3 flex-grow-1 overflow-auto" id="type-list" style="max-height:300px;">
  182. {% for ct in contract_types %}
  183. <li class="list-group-item d-flex justify-content-between align-items-center">
  184. <div>
  185. <strong>{{ ct.name }}</strong>
  186. {% if ct.prefix %}<span class="text-muted">[前缀: {{ ct.prefix }}]</span>{% endif %}
  187. </div>
  188. <div>
  189. <button class="btn btn-sm btn-secondary edit-prefix-btn" data-id="{{ ct.id }}" data-prefix="{{ ct.prefix or '' }}">设置前缀</button>
  190. <button class="btn btn-sm btn-danger delete-btn" data-id="{{ ct.id }}">删除</button>
  191. </div>
  192. </li>
  193. {% endfor %}
  194. </ul>
  195. <div class="input-group mt-auto">
  196. <input type="text" id="new-type" class="form-control" placeholder="新增合同类型">
  197. <button class="btn btn-primary" id="add-type">添加</button>
  198. </div>
  199. </div>
  200. </div>
  201. </div>
  202. </div>
  203. </div>
  204. <script>
  205. // 添加部门/主体/类型
  206. function addItem(url, inputId) {
  207. let name = document.getElementById(inputId).value.trim();
  208. if(!name) return alert('请输入名称');
  209. fetch(url, {
  210. method:'POST',
  211. headers:{'Content-Type':'application/json'},
  212. body:JSON.stringify({name})
  213. }).then(res=>location.reload());
  214. }
  215. document.getElementById('add-department').addEventListener('click', ()=>addItem('/system/config/department/add','new-department'));
  216. document.getElementById('add-entity').addEventListener('click', ()=>addItem('/system/config/entity/add','new-entity'));
  217. document.getElementById('add-type').addEventListener('click', ()=>addItem('/system/config/type/add','new-type'));
  218. // 设置前缀
  219. document.querySelectorAll('.edit-prefix-btn').forEach(btn=>{
  220. btn.addEventListener('click', function(){
  221. const id=this.dataset.id;
  222. const currentPrefix=this.dataset.prefix;
  223. const newPrefix=prompt("请输入合同类型前缀:", currentPrefix);
  224. if(newPrefix===null) return;
  225. fetch(`/system/config/type/set_prefix/${id}`,{
  226. method:'POST',
  227. headers:{'Content-Type':'application/json'},
  228. body:JSON.stringify({prefix:newPrefix})
  229. }).then(res=>location.reload());
  230. });
  231. });
  232. // 删除
  233. document.querySelectorAll('.delete-btn').forEach(btn=>{
  234. btn.addEventListener('click', function(){
  235. const id=this.dataset.id;
  236. const cardHeader=this.closest('.card').querySelector('.card-header').innerText;
  237. const type = cardHeader.includes('部门') ? 'department' : cardHeader.includes('主体') ? 'entity' : 'type';
  238. fetch(`/system/config/${type}/delete/${id}`, {method:'POST'}).then(res=>location.reload());
  239. });
  240. });
  241. // 显示/隐藏频率相关选项
  242. document.querySelectorAll('input[name="frequency"]').forEach(radio => {
  243. radio.addEventListener('change', function() {
  244. document.getElementById('weekly-options').style.display =
  245. (this.value === 'weekly') ? 'block' : 'none';
  246. document.getElementById('monthly-options').style.display =
  247. (this.value === 'monthly') ? 'block' : 'none';
  248. });
  249. });
  250. // 处理定时设置表单提交
  251. document.getElementById('scheduler-form').addEventListener('submit', function(e) {
  252. e.preventDefault();
  253. const formData = new FormData(this);
  254. const data = {
  255. frequency: formData.get('frequency'),
  256. weekdays: formData.getAll('weekdays').map(Number),
  257. month_day: parseInt(formData.get('month_day')),
  258. hour: parseInt(formData.get('hour')),
  259. minute: parseInt(formData.get('minute'))
  260. };
  261. fetch('/system/config/scheduler', {
  262. method: 'POST',
  263. headers: {'Content-Type': 'application/json'},
  264. body: JSON.stringify(data)
  265. })
  266. .then(response => {
  267. if (response.ok) {
  268. alert('定时设置已保存');
  269. location.reload();
  270. } else {
  271. alert('保存失败,请重试');
  272. }
  273. });
  274. });
  275. </script>
  276. <style>
  277. /* 卡片内部列表滚动 */
  278. .list-group {
  279. max-height: 300px;
  280. overflow-y: auto;
  281. }
  282. /* 条目间距与按钮大小 */
  283. .list-group-item .btn {
  284. margin-left: 5px;
  285. }
  286. /* 标题加粗 */
  287. .card-header, h3 {
  288. font-weight: 600;
  289. }
  290. /* 表单与列表分离 */
  291. .card-body input.form-control {
  292. min-width: 0;
  293. }
  294. /* 按钮圆角和阴影 */
  295. .btn-primary, .btn-danger, .btn-secondary {
  296. border-radius: 0.35rem;
  297. }
  298. /* 频率选项间距 */
  299. .form-check {
  300. margin-bottom: 0.5rem;
  301. }
  302. /* 星期选项布局 */
  303. #weekly-options .col-4 {
  304. margin-bottom: 0.5rem;
  305. }
  306. </style>
  307. {% endblock %}